链表是 一种常见的线性数据结构,没有特殊要求的链式表。
存放数据的基本单位也是节点。
地址 不连续的节点 序列,彼此通过 指针 相互连接。
单向线性链表:
单向循环链表
双向线性
双线循环
数组链表
链表数组
二维链表
数组链表---元素是数组的链表
struct Node{
int data[5];
struct Node* next;
};
链表数组---元素是链表的数组
struct Linked{};
struct Linked arr[5];
最常见的链表就是 单向线性链表和双向线性链表
写一个双向链表:
节点:
struct ListNode{
int data;
struct ListNode* next;//下一个节点
struct ListNode* prev;//上一个节点
};
链表:
Struct List{
struct ListNode* head;//链表头
struct ListNode* tail;//链表尾,方便追加
struct ListNode* frwd;//从前向后查找的位置,和链表头不一样,
//在查找时frwd是可以改变。
//正向迭代指针,提供遍历。
struct ListNode* bkwd;//从后向前查找的位置
};
遍历:从头到尾找一遍
迭代:一个挨着一个 进行遍历
特点:
第一个没有前指针
最后一个没有后指针
当然,可以在前面加一个,也可以在后面加一个
可以有下标,一个挨着一个
链表增删方便,随机访问不方便
增删元素的函数:
追加元素---修改原来的tail的next指向新节点,链表的tail也指向新节点。
插入元素---新节点前的节点改next,新节点后的节点改prev,新节点自身的前后设置好
删除元素---删除节点前的节点改next,删除节点后的节点改prev,把删除节点内存回收。
返回数据的两种方式:
1、 用return语句返回数据
2、 用 指针做参数 传入数据的地址,把 数据 带回来。
------------------------------------------------------------------------
1 #ifndef _LS_H
2 #define _LS_H
3 #include <sys/types.h>
4 typedef struct ListNode{//双向链表节点
5 int data;//数据
6 struct ListNode* next;//后节点
7 struct ListNode* prev;//前节点
8 }LISTNODE;
9 typedef struct List{
10 LISTNODE* head;//头节点
11 LISTNODE* tail;//尾节点
12 LISTNODE* frwd;//正向查询 迭代节点
13 LISTNODE* bkwd;//反向查询 迭代节点(暂时不用)
14 }LIST;
15 void list_init(LIST* list);//初始化 空链表
16 void list_deinit(LIST* list);//释放所有的链表 回空
17 int list_empty(LIST* list);//判断是否空
18 void list_append(LIST* list,int data);//追加元素
19 //前插(pos之前)
20 int list_insert(LIST* list,size_t pos,int data);//插入元素
21 int list_erase(LIST* list,size_t pos);//按位置删除
22 void list_remove(LIST* list,int data);//按数据删除
23 int * list_at(LIST* list,size_t pos);//按位置取数,不删除
24 void list_clear(LIST* list);//清空所有元素
25 size_t list_size(LIST* list);//元素个数
26 //设计一下正向迭代算法(提高取数据的效率)
27 void list_begin(LIST* list);//回头,开始正向迭代(从头开始)
28 int * list_next(LIST* list);//向next移动一下并返回移动之前的数据
29 int * list_prev(LIST* list);//向prev移动一下并返回移动之前的数据
30 int * list_current(LIST* list);//不移动 直接取数据
31 int list_end(LIST* list);//判断是否迭代结束
32 #endif
---------------------------------------------------------------------------
ls.c
1 #include <stdlib.h>
2 #include "ls.h"
3
4 static LIST_NODE * create_node(int data,
5 LIST_NODE* prev,LIST_NODE* next){
6 LIST_NODE* node = malloc(sizeof(LIST_NODE));
7 node->data = data;
8 node->prev = prev;
9 node->next = next;
10 return node;
11 }//回收节点时,返回前节点和后节点指针
12 static LIST_NODE* destroy_node(LIST_NODE* node,LIST_NODE** prev){
13 //next被return,prev用2级指针
14 LIST_NODE* next = node->next;
15 if (prev) *prev = node->prev;//prev不为空带出前节点
free(node);
16 return next;
17 }
18 void list_init(LIST * list){//初始化 空链表
19 list->head = NULL;
20 list->tail = NULL;
21 }
22 void list_deinit(LIST * list){//释放所有的链表 回空
23 while(list->head)//从前向后删除,不需要前节点信息,所有第二个参数为NULL
24 {
25 list->head = destroy_node(list->head,NULL);
26 }
27 list->tail = NULL;
28 }
29 int list_empty(LIST* list){//判断是否空
30 return (!list->head) && (!list->tail);
31 }
32 void list_append(LIST* list,int data){//追加元素
33 list->tail = create_node(data,list->tail,NULL);
34 //list->tail链表指向新节点
35 //list->tail->prev就是原来链表的最后节点
36 if(list->tail->prev)//非空
37 list->tail->prev->next = list->tail;
38 else//空的话,表示没有节点,则改变链表的头节点
39 list->head = list->tail;
40
41 }
42 //前插元素(pos之前)
43 int list_insert(LIST* list,size_t pos,int data){//插入元素
44 LIST_NODE* find = NULL;//head和tail不变,find可变;
45 //因为head不能动,只能用find来动来进行遍历
46 for (find=list->head;find;find=find->next)
47 {//从前向后开始走,一步一步往下走,直到find为空(大范围操作)
48 if (!pos--){//找到位置,pos=0,建节点;
49 //如果pos参数为10的话,在这里find需要走11次才能插入
50 //找到位置后,建立节点
51 LIST_NODE* node = create_node(data,find->prev,find);
52 //找到find位置,则数据为data,find->prev前节点,find后节点
53 if (node->prev) //node->prev不为空,则不是插入第一个位置
54 {
55 node->prev->next = node;
56 }else//为空,则将list-head设置为新节点即可。
57 list->head = node;
58 node->next->prev = node;//改的后节点
59 return 1;//代表插入成功
60 }
61 }
62 return 0;//代表插入失败
63 }
64 int list_erase(LIST* list,size_t pos){//按位置删除
65 LIST_NODE * find = NULL;
66 for (find=list->head;find;find=find->next)
67 {
68 if (!pos--)
69 {
70 LIST_NODE * prev = NULL;
71 LIST_NODE * next =destroy_node(find,&prev);
72 if (prev)//判断不是头节点
73 prev->next = next;//改变前节点的next;
74 else//如果删除的是头节点
75 list->head = next;
76 if (next)//判断不是尾节点
77 next->prev = prev;
78 else
79 list->tail = prev;
80 return 1;
81 }
82 }
83 return 0;
84 }
85 void list_remove(LIST* list,int data){//按数据删除
86 LIST_NODE * find = NULL, *findnext = NULL;
87 for (find=list->head;find;find=findnext)
88 {
89 findnext=find->next;//先保存,否则销毁find就执行不下去了。
90 if (find->data == data)//找到了数据
91 {
92 LIST_NODE* prev = NULL;
93 LIST_NODE* next = destroy_node(find,&prev);
94 if (prev) prev->next = next;
95 else list->head = next;
96 if (next) next->prev = prev;
97 else list->tail = prev;
98 }
99 }
100 }
101 //直接返回int,就没法确定是不是 找到的数据了。返回的0是数值还是位置呢。
102 int * list_at(LIST* list,size_t pos){//按位置取数,不删除
103 //为什么这里是int *,而不是int
104 //如果是int,万一提供的pos越界了,则NULL。
105 LIST_NODE * find = NULL;
106 for(find=list->head;find;find=find->next)
107 {
108 if (!pos--)
109 {
110 return &(find->data);
111 }
112 }
113 return NULL;//NULL可以代表没有找到。
114 }
115 void list_clear(LIST* list){//清空所有元素
116 list_deinit(list);
117 }
118 size_t list_size(LIST* list){//元素个数
119 size_t size = 0;
120 LIST_NODE * node = NULL;
121 for (node=list->head;node;node=node->next)
122 {
123 size++;
124 }
125 return size;
126 }
127 void list_begin(LIST* list){//开始正向迭代
128 list->frwd=list->head;//从头开始
129 }
130 int * list_next(LIST* list){//向next移动了一下
131 int * data = &(list->frwd->data);
132 list->frwd = list->frwd->next;
133 return data;
134 }
135 int * list_prev(LIST* list){//向prev移动了一下
136 int * data = &(list->frwd->data);
137 list->frwd = list->frwd->prev;
138 return data;
139 }
140 int * list_current(LIST* list){//不移动,直接取当前数据
141 return &(list->frwd->data);
142 }
143 int list_end(LIST* list){//判断是否结束
144 return !list->frwd;
145 }
------------------------------------------------------------------------
测试用例代码:
1 #include <stdio.h>
2 #include "ls.h"
3 #include <stdlib.h>
4 int main()
5 {
6 LIST list;
7 list_init(&list);
8 list_append(&list,10);list_append(&list,30);list_append(&list,50);
9 list_insert(&list,1,20);list_insert(&list,3,40);
10 printf("size=%d\n",list_size(&list));
11 int i;//这种查找方式 效率 问题很大,每次都从头开始。
12 for(i=0;i<list_size(&list);i++)
13 printf("%d\n",*(list_at(&list,i)));
14 list_erase(&list,1);//删除了20
15 printf("删除了20------------------------------\n");
16 for(i=0;i<list_size(&list);i++)
17 printf("%d\n",*(list_at(&list,i)));
18 printf("追加40------------------------------------\n");
19 list_append(&list,40);
20 for(i=0;i<list_size(&list);i++)
21 printf("%d\n",*(list_at(&list,i)));
22 printf("找到40删除所有的40----------------------------------\n");
23 list_remove(&list,40);//应该删除2个40
24
25 for(i=0;i<list_size(&list);i++)
26 printf("%d\n",*(list_at(&list,i)));
27 list_deinit(&list);
28 return 0;
29 }
---------------------------------------------------------------------
songxl@ubuntu:~/ds/day03$ gcc testls.c ls.c
songxl@ubuntu:~/ds/day03$ a.out
size=5
10
20
30
40
50
删除了20------------------------------
10
30
40
50
追加40------------------------------------
10
30
40
50
40
找到40删除所有的40----------------------------------
10
30
50
-----------------------------------------------------------------
测试用例2
1 #include <stdio.h>
2 #include "ls.h"
3 #include <stdlib.h>
4 int main()
5 {
6 LIST list;
7 list_init(&list);
8 list_append(&list,10);list_append(&list,30);list_append(&list,50);
9 list_insert(&list,1,20);list_insert(&list,3,40);
10 printf("size=%d\n",list_size(&list));
11 list_begin(&list);//先从头开始
12 while(!list_end(&list))//不到尾节点就循环
13 printf("%d\n",*(list_next(&list)));
14 list_erase(&list,1);//删除了20
15 printf("删除了20------------------------------\n");
16 list_begin(&list);//先从头开始
17 while(!list_end(&list))//不到尾节点就循环
18 printf("%d\n",*(list_next(&list)));
19 printf("追加40------------------------------------\n");
20 list_append(&list,40);
21 printf("找到40删除所有的40----------------------------------\n");
22 list_remove(&list,40);//应该删除2个40
23
24 list_begin(&list);//每次迭代都要先从头开始
25 while(!list_end(&list))//不到尾节点就循环
26 printf("%d\n",*(list_next(&list)));
27 list_deinit(&list);
28 return 0;
29 }