0.大忌

1.更新到下一个链表

head=head->next;而不是head=head->data;
另外:不要忘记更新!!读取完后就应该更新,不然直接TLE或者RuntimeError

2.以一个元素创建一个新列表时忘记返回newnode

严重的错误,不过估计编译器也会报错

3.使用head和tail的话不需要用malloc分配内存,直接赋值NULL

否则会超时

1.头文件与模型定义

1
2
3
4
5
6
7
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node{
    int data;
    struct Node* next;
}Node;

next不要写错,然后分号别忘,没有typedef为了方便可以自己弄个
结构体里面是指针,不要漏了 *号

2.创建链表元素

以data为一个整数为例:

可以按需求修改函数

 

1
2
3
4
5
6
 Node* Createnode(int x){
     Node* newnode=(Node*)malloc(sizeof(Node));
     newnode->data=x;
     newnode->next=NULL;
     return newnode;
 }

返回值不要漏掉!链表最容易漏掉这个,最好先写这个

新生成的链表下一个是NULL,便于后续操作指向下一链表

创建列表的动态内存分配还是要注意怎么写,能最后free一下最好就free一下吧。

3.输入输出

以单整数链表的输入输出为例子

1
2
3
4
5
6
7
8
//输出
void Printnode(Node* head){
    while(head!=NULL){
        printf("%d ",head->data);
        head=head->next;
    }
    printf("\n");
}

输出大差不差,有时会用->作为连接
下面看看输入:

1.scanf+getchar检测换行符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//输入
void Inputnode(void){
    int temp=0;
    Node* head=NULL;
    Node* tail=NULL;
    while(scanf("%d",&temp)==1){
        Node* newnode=Createnode(temp);
        if(head==NULL){
            head=tail=newnode;
        }
        else{
            tail->next=newnode;
            tail=newnode;
        }
    }
}

有一个问题是,必须输入一行后接上换行符

如果最后一行没有接上换行符就不行了

scanf不要漏掉换行符

2.fgets+strtok

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Node* Inputnode(void){
    char buffer[1024];//这里可以改,要多大有多大
    Node* head=NULL;
    Node* tail=NULL;
    if(fgets(buffer,sizeof(buffer),stdin)){
        char* token=strtok(buffer," ");
        while(token){
            int temp=atoi(token);
            Node* newnode=Createnode(temp);
            if(head==NULL){
              head=tail=newnode;
            }
            else{
                tail->next=newnode;
                tail=newnode;
            }
            token=strtok(NULL," ");
        }
    }
    return head;//一定要记得return
}

这个更加稳定()

3.构建链表基本框架

1.设置头尾+自行判断

1
2
3
4
5
6
7
8
9
10
11
Node* head=NULL;
Node* tail=NULL;
Node* newnode=____;
if(head==NULL){
    head=tail=newnode;
}
else{
    tail->next=newnode;
    tail=newnode;
}
return head;

==注意相等是两个等号==

2.哑巴节点

指向头节点,可以不必判断头结点的特殊情况了

4.基本函数

链表函数基本上返回的都是指针,应该写Node*

插入链表元素

1
2
3
4
5
6
7
8
9
Node* insert(Node* head,Node* insert,int m){
Node* p=head;
for(int i=0;i<m-1;i++){//要看好插入的位置,此处插在右边
p=p->next;
}
insert->next=p->next->next;
p->next=insert;
return head;
}

删除链表元素

此处为删除重复元素

先跳线,在炸掉桥,再把现在的由之前的来定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define Max 100

void deleteDuplicates(struct Node* head){
int* a=(int*)calloc(Max,sizeof(int));
Node* pre=NULL;
    Node* curr=head;
    while(curr!=NULL){
        if(a[curr->data]==0){
            a[curr->data]=1;
    pre=curr;
  curr=curr->next;
    }
        else if(a[curr->data]==1){
            pre->next=curr->next;  
            free(curr);
            curr=pre->next;            
        }
    }
    free(a);
}

整数链表合并函数(正序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Node* mergenode(Node* node1, Node* node2) {
    Node* p1 = node1;
    Node* p2 = node2;
    Node* head = NULL;
    Node* tail = NULL;
    while (p1 && p2) {
        Node* newnode;
        if (p1->data <=p2->data) {
            newnode = Createnode(p1->data);
            p1 = p1->next;
        }
        else {
            newnode = Createnode(p2->data);
            p2 = p2->next;
        }
        if (head == NULL) {
            head = tail = newnode;
        }
        else {
            tail->next = newnode;
            tail = newnode;
        }
    }

    if (p1 == NULL) {
        tail->next = p2;
    }
    else {
        tail->next = p1;
    }
    return head;
}

指定位置逆序

详细注释版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Node* Inversenode(Node* node, int m, int n) {
    // 如果 m == n,表示区间不需要反转,直接返回原链表
    if (m == n) return node;
    // 创建一个哑结点(dummy node),用于简化链表边界处理
    // 哑结点的 next 指向链表的头部
    Node* dummy = (Node*)malloc(sizeof(Node));
    dummy->next = node;
    // pre 用于记录反转区间的前一个节点
Node* pre = dummy;

    // 移动 pre 指针到反转区间的前一个节点,pre 就是反转区间前一个节点

    // 例如,如果 m=2,pre 最终会指向第一个节点

    for (int i = 1; i < m; i++) {
        pre = pre->next;  // 移动 pre 到第 m-1 个节点
    }

    // start 是反转区间的第一个节点,then 是 start 后面的节点(即第一个需要反转的节点)
    Node* start = pre->next;
    Node* then = start->next;
    // 反转区间内的节点
    // 通过改变指针顺序来完成反转。反转的范围是从 m 到 n
    // 反转 n - m 次
    for (int i = 0; i < n - m; i++) {
        // 步骤 1:start->next 指向 then->next,即跳过 then
        start->next = then->next;

        // 步骤 2:then->next 指向 pre->next,即将 then 放到反转区间的最前面

        then->next = pre->next;

        // 步骤 3:pre->next 指向 then,即把 pre 连接到 then 上

        pre->next = then;

        // 步骤 4:then 移动到下一个节点,准备进行下一次反转

        then = start->next;  // 重新指向 start 的下一个节点
    }

    // 返回新的链表头(dummy->next)
    Node* result = dummy->next;

    // 释放哑结点
    free(dummy);
   
    return result;
}

辅助理解动画:[终于把单链表反转搞明白了(一)_带头节点的单链表原地反转_哔哩哔哩_bilibili]

模板精简版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Node* Inversenode(Node* node,int m,int n){
    if(m==n) return node;
    Node* dummy=(Node*)malloc(sizeof(Node));
    dummy->next=node;
    Node* pre=dummy;
    for(int i=1;i<m;i++){
        pre=pre->next;
    }
    Node* start=pre->next;
    Node* then=start->next;
    for(int i=0;i<n-m;i++){
        start->next=then->next;
        then->next=pre->next;
        pre->next=then;
        then=start->next;
    }
    Node* res=dummy->next;
    free(dummy);
    return res;
}