一.单向链表:
由于线性表的顺序存储结构(数组),在插入和删除需要移动大量元素以及其他特殊情况下,效率低下,耗时费力,此处引入了链表的概念。
链表的基本组成单元为结点(node),包括两个域:存储数据元素信息的域->数据域;存储直接后继存储位置的域(指针)->指针域。
1 #include2 #include 3 #include 4 #define OK 1 5 #define ERROR 0 6 7 8 typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */ 9 typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */ 10 11 typedef struct Node 12 { 13 ElemType data; 14 struct Node *next; 15 }Node; 16 typedef struct Node *LinkList; //定义LinkList 17 18 Status InitList(LinkList *L); 19 Status Destroy(LinkList L); 20 Status ClearList(LinkList *L); 21 bool ListEmpty(LinkList L); 22 int ListLength(LinkList L); 23 Status GetElem(LinkList L,int i,ElemType *e); 24 int LocateElem(LinkList L,ElemType e); 25 Status PriorElem(LinkList *L, ElemType cur_e, LinkList *pre_e); 26 Status NextElem(LinkList *L, ElemType cur_e, LinkList *next_e); 27 Status ListInsert(LinkList *L,int i,ElemType e); 28 Status ListDelete(LinkList *L,int i,ElemType *e); 29 Status ListTraverse(LinkList L); 30 void CreateListHead(LinkList *L, int n); 31 void CreateListTail(LinkList *L, int n); 32 33 Status visit(ElemType c) 34 { 35 printf("%d ",c); 36 return OK; 37 } 38 39 /*初始化顺序线性表: 构造一个空的线性表L*/ 40 Status InitList(LinkList *L) 41 { 42 *L = (LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点,L为头指针 */ 43 if(!(*L)) /* 存储分配失败 */ 44 return ERROR; 45 (*L)->next = NULL; /* 指针域为空 */ 46 47 return OK; 48 } 49 /*初始条件:线性表L已存在。操作结果:销毁线性表L*/ 50 Status Destroy(LinkList L) 51 { 52 ClearList(&L); 53 free(L); 54 L = NULL; 55 } 56 57 /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */ 58 Status ClearList(LinkList *L) 59 { 60 LinkList p,q; 61 p=(*L)->next; /* p指向第一个结点 */ 62 while(p) /* 没到表尾 */ 63 { 64 q=p->next; 65 free(p); 66 p=q; 67 } 68 (*L)->next = NULL; /* 头结点指针域为空 */ 69 return OK; 70 } 71 72 /* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */ 73 bool ListEmpty(LinkList L) 74 { 75 if(L->next) 76 return false; 77 else 78 return true; 79 } 80 81 /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */ 82 int ListLength(LinkList L) 83 { 84 int i = 0; 85 LinkList p=L->next; /* p指向第一个结点 */ 86 while(p) 87 { 88 i++; 89 p=p->next; 90 } 91 return i; 92 } 93 94 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 95 操作结果:用e返回L中第i个数据元素的值 */ 96 Status GetElem(LinkList L,int i,ElemType *e) 97 { 98 int j = 1; /* j为计数器 */ 99 LinkList p; /* 声明一结点p */ 100 p = L->next; /* 让p指向链表L的第一个结点 */ 101 while (p && j < i) { /* p不为空或者计数器j还没有等于i时,循环继续 */ 102 p = p->next; /* 让p指向下一个结点 */ 103 j++; 104 } 105 if ( !p || j>i ) 106 return ERROR; /* 第i个元素不存在 */ 107 *e = p->data; /*取第i个元素的数据 */ 108 return OK; 109 } 110 111 /* 初始条件:顺序线性表L已存在 112 操作结果:返回L中第1个与e满足关系的数据元素的位序。 113 若这样的数据元素不存在,则返回值为0 */ 114 int LocateElem(LinkList L,ElemType e) 115 { 116 int i=0; 117 LinkList p=L->next; 118 while(p) 119 { 120 i++; 121 if(p->data==e) /* 找到这样的数据元素 */ 122 return i; 123 p=p->next; 124 } 125 126 return 0; 127 } 128 129 /*初始条件:线性表L已存在 130 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, 131 否则操作失败,pre_e无定义*/ 132 Status PriorElem(LinkList *L, ElemType cur_e, LinkList *pre_e) 133 { 134 LinkList p; 135 p = (*L)->next; 136 if(p->data == cur_e) { /* 是第一个 */ 137 pre_e = NULL; 138 return ERROR; 139 } 140 while(p->next) 141 { 142 if(p->next->data == cur_e) { 143 *pre_e = p; 144 return OK; 145 } 146 p = p->next; 147 } 148 pre_e = NULL; 149 return ERROR; 150 } 151 152 /*初始条件:线性表L已存在 153 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后驱, 154 否则操作失败,next_e无定义*/ 155 Status NextElem(LinkList *L, ElemType cur_e, LinkList *next_e) 156 { 157 LinkList p; 158 p = (*L); 159 while(p->next) 160 { 161 if(p->data == cur_e) { 162 *next_e = p->next; 163 return OK; 164 } 165 p = p->next; 166 } 167 *next_e = NULL; 168 return ERROR; 169 } 170 171 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) + 1, 172 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ 173 Status ListInsert(LinkList *L,int i,ElemType e) 174 { 175 int j = 1; 176 LinkList p,s; 177 p = *L; 178 while (p && j < i) { /* 寻找第i个结点 */ 179 p = p->next; 180 ++j; 181 } 182 if (!p || j > i) 183 return ERROR; /* 第i个元素不存在 */ 184 s = (LinkList)malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */ 185 s->data = e; 186 s->next = p->next; /* 将p的后继结点赋值给s的后继 */ 187 p->next = s; /* 将s赋值给p的后继 */ 188 return OK; 189 } 190 191 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 192 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ 193 Status ListDelete(LinkList *L,int i,ElemType *e) 194 { 195 int j; 196 LinkList p,q; 197 p = *L; 198 j = 1; 199 while (p->next && j < i) { /* 遍历寻找第i个元素 */ 200 p = p->next; 201 ++j; 202 } 203 if (!(p->next) || j > i) 204 return ERROR; /* 第i个元素不存在 */ 205 q = p->next; 206 p->next = q->next; /* 将q的后继赋值给p的后继 */ 207 *e = q->data; /* 将q结点中的数据给e */ 208 free(q); /* 让系统回收此结点,释放内存 */ 209 return OK; 210 } 211 212 /* 初始条件:顺序线性表L已存在 213 操作结果:依次对L的每个数据元素输出 */ 214 Status ListTraverse(LinkList L) 215 { 216 LinkList p=L->next; 217 while(p) 218 { 219 visit(p->data); 220 p=p->next; 221 } 222 printf("\n"); 223 return OK; 224 } 225 226 /* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */ 227 void CreateListHead(LinkList *L, int n) 228 { 229 LinkList p; 230 int i; 231 srand(time(0)); /* 初始化随机数种子 */ 232 *L = (LinkList)malloc(sizeof(Node)); 233 (*L)->next = NULL; /* 先建立一个带头结点的单链表 */ 234 for (i=0; i ) 235 { 236 p = (LinkList)malloc(sizeof(Node)); /* 生成新结点 */ 237 p->data = rand()%100+1; /* 随机生成100以内的数字 */ 238 p->next = (*L)->next; 239 (*L)->next = p; /* 插入到表头 */ 240 } 241 } 242 243 /*建立带表头结点的单链线性表L(尾插法) */ 244 void CreateListTail(LinkList *L, int n) 245 { 246 LinkList p,r; 247 *L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */ 248 r = *L; /* r为指向尾部的结点 */ 249 for (int i = 0; i < n; i++) { 250 p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */ 251 scanf("%d",p->data); 252 r->next = p; /* 将表尾终端结点的指针指向新结点 */ 253 r = p; /* 将当前的新结点定义为表尾终端结点 */ 254 } 255 r->next = NULL; /* 表示当前链表结束 */ 256 } 257 int main() 258 { 259 LinkList L, pre_e,next_e; //创建一个可以指向结点的指针 260 CreateListTail(&L,10); 261 ListInsert(&L,11,55); 262 ListTraverse(L); 263 PriorElem(&L, 3, &pre_e); 264 NextElem(&L, 3, &next_e); 265 printf("前驱%d\n",(*pre_e).data); 266 printf("后继%d\n",(*next_e).data); 267 Destroy(L); 268 269 270 return 0; 271 }
单向链表的基本结构:
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList; //定义LinkList
单向链表的基本操作:/*只需浏览,作了解,详见(代码:sj1_0)*/
Status InitList(LinkList *L);
操作结果:构造一个空的线性表。
Status Destroy(LinkList L);
初始条件:线性表L已存在。
操作结果:销毁线性表L
Status ClearList(LinkList *L);
初始条件:线性表L已存在。
操作结果:将L重置为空表
Status ListEmpty(LinkList L);
初始条件:线性表L已存在。
操作结果:若L为空表,则返回TRUE,否则返回FALSE。
int ListLength(LinkList L);
初始条件:线性表L已存在。
作结果:返回L中数据元素个数。
Status GetElem(LinkList L, int i, ElemType *e);
初始条件:顺序线性表L已存在,1≤i≤ListLength(L)。
操作结果:用e返回L中第i个数据元素的值。
int LocateElem(LinkList L, ElemType e);
初始条件:顺序线性表L已存在。
操作结果:返回L中第1个与e满足关系的数据元素的位序。
若这样的数据元素不存在,则返回值为0。
Status PriorElem(LinkList *L, ElemType cur_e, LinkList *pre_e);
初始条件:线性表L已存在
操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
否则操作失败,pre_e无定义。
Status NextElem(LinkList *L, ElemType cur_e, LinkList *next_e);
初始条件:线性表L已存在
操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后驱,
否则操作失败,next_e无定义。
Status ListInsert(LinkList *L, int i, ElemType e);
初始条件:顺序线性表L已存在,1 ≤i ≤ ListLength(L) + 1。
操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。
Status ListDelete(LinkList *L, int i, ElemType *e);
初始条件:顺序线性表L已存在,1≤i≤ListLength(L)。
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1。
Status ListTraverse(LinkList L);
初始条件:顺序线性表L已存在。
操作结果:依次对L的每个数据元素输出。
void CreateListHead(LinkList *L, int n);
操作结果:立带表头结点的单链线性表L(头插法)。
void CreateListTail(LinkList *L, int n);
操作结果:建立带表头结点的单链线性表L(尾插法)。
1.单链表的读取
算法思路:
1.声明一个指正p指向链表的第一个结点,初始化j = 1。
2.当jnext; j++;
3.若链表末尾p为空,则说明第i个结点不存在。
4.否则查找成功,返回结点p的数据。
1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 2 操作结果:用e返回L中第i个数据元素的值 */ 3 Status GetElem(LinkList L,int i,ElemType *e) 4 { 5 int j = 1; /* j为计数器 */ 6 LinkList p; /* 声明一结点p */ 7 p = L->next; /* 让p指向链表L的第一个结点 */ 8 while (p && j < i) { /* p不为空或者计数器j还没有等于i时,循环继续 */ 9 p = p->next; /* 让p指向下一个结点 */ 10 j++; 11 } 12 if ( !p || j>i ) 13 return ERROR; /* 第i个元素不存在 */ 14 *e = p->data; /*取第i个元素的数据 */ 15 return OK; 16 }
2.单链表的插入
算法思路:
1.声明一个指针p指向链表头结点,向后遍历p=p->next,直到满足条件。
2.新建一个结点s。
3.s->next = p->next ①
4.p->next = s ②③
1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) + 1, 2 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ 3 Status ListInsert(LinkList *L,int i,ElemType e) 4 { 5 int j = 1; 6 LinkList p,s; 7 p = *L; 8 while (p && j < i) { /* 寻找第i个结点 */ 9 p = p->next; 10 ++j; 11 } 12 if (!p || j > i) 13 return ERROR; /* 第i个元素不存在 */ 14 s = (LinkList)malloc(sizeof(Node)); /* 生成新结点 */ 15 s->data = e; 16 s->next = p->next; /* 将p的后继结点赋值给s的后继 */ 17 p->next = s; /* 将s赋值给p的后继 */ 18 return OK; 19 }
3.单链表的删除
算法思路:
1. 声明一个指针p指向链表头结点,向后遍历p=p->next,直到满足条件。
2. q = p->next
3. p->next = q->next ①②
4. free(q) ③④
1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 2 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ 3 Status ListDelete(LinkList *L,int i,ElemType *e) 4 { 5 int j; 6 LinkList p,q; 7 p = *L; 8 j = 1; 9 while (p->next && j < i) { /* 遍历寻找第i个元素 */ 10 p = p->next; 11 ++j; 12 } 13 if (!(p->next) || j > i) 14 return ERROR; /* 第i个元素不存在 */ 15 q = p->next; 16 p->next = q->next; /* 将q的后继赋值给p的后继 */ 17 *e = q->data; /* 将q结点中的数据给e */ 18 free(q); /* 让系统回收此结点,释放内存 */ 19 return OK; 20 }
4.单链表的整体创建
算法思路:
1.创建指向头结点的头指针L,使r=L
While(){
2.新建结点p
3.r->next = p
4.r = p
}如此往复
5.最后一个结点指针指向NULL
1 /*建立带表头结点的单链线性表L(尾插法) */ 2 void CreateListTail(LinkList *L, int n) 3 { 4 LinkList p,r; 5 *L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */ 6 r = *L; /* r为指向尾部的结点 */ 7 for (int i = 0; i < n; i++) { 8 p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */ 9 scanf("%d",p->data); 10 r->next = p; /* 将表尾终端结点的指针指向新结点 */ 11 r = p; /* 将当前的新结点定义为表尾终端结点 */ 12 } 13 r->next = NULL; /* 表示当前链表结束 */ 14 }
总体思路就是如此。理解模型后,其他操作原理相同。
practice:
1.已知线性表中的元素以值的递增有序排列,并以单链表做存储结构。试写一高效的算法,删除表中所有大于mink且小于maxk的元素(若表中存在这样德尔元素),同时释放被删结点空间,并分析你的算法复杂度(注意:mink 和 maxk 是给定的两个参变量,它们的值可以和表中元素相同,也可以不同)。代码(代码:sj1_1)
1 //可以追溯到短学期写的 2 #include3 #include 4 5 struct line 6 { 7 int num; 8 struct line *next; 9 }; 10 typedef struct line *LinkList; 11 LinkList head; 12 13 struct line *creat(); 14 struct line *cut(int min,int max); 15 void display(); 16 int main() 17 { 18 int mink,maxk; 19 creat(); 20 display(); 21 printf("Plead input mink & maxk:"); 22 scanf("%d %d",&mink,&maxk); 23 cut(mink,maxk); 24 display(); 25 return 0; 26 } 27 28 struct line *creat() 29 { 30 LinkList p1, p2; 31 int count; 32 printf("Please input the number of JieDian:"); 33 scanf("%d",&count); 34 p1 = p2 =(struct line *)malloc(sizeof(struct line)); 35 scanf("%d",&p1->num); 36 head = p1; 37 for(int i = 1;i < count;i++) 38 { 39 p2 =(struct line *)malloc(sizeof(struct line)); 40 scanf("%d",&p2->num); 41 p1->next = p2; 42 p1 = p2; 43 } 44 p2->next = NULL; 45 return head; 46 } 47 struct line *cut(int min,int max) 48 { 49 LinkList p, q; 50 p = head; 51 while(p->next->num <= min) 52 p = p->next; 53 while(p->next->num > min && p->next->num < max) 54 { 55 q = p->next; 56 p->next = q->next ; 57 free(q); 58 } 59 q = NULL; // free(q)只是释放了q所指的内存空间。 q = NULL防止发生q为野指针 60 return head; 61 } 62 63 void display() 64 { 65 LinkList p; 66 p = head; 67 if(p == NULL) 68 printf("NULL!"); 69 else 70 { 71 while(p != NULL) 72 { 73 printf("%d ",p->num); 74 p = p->next; 75 } 76 } 77 printf("\n"); 78 }
2.试写一算法,实现顺序表的就地置逆(不开辟新空间),即利用原表的存储空间将线性表(a1,a2,…,an)逆置为(an,an-1,…,a1).
3.试写一算法,对单链表实现就地置逆。(代码:sj1_2)
1 /*短学期写的。。。 2 单链表就地逆置(不开辟新空间) Change() 3 头结点不存num数据*/ 4 #include5 #include 6 7 struct line 8 { 9 int num; 10 struct line* next; 11 }; 12 struct line *head; 13 struct line *creat(); 14 void Change(); 15 void display(); 16 int main() 17 { 18 creat(); 19 printf("Original data:"); 20 display(); 21 Change(); 22 printf("Changed data:"); 23 display(); 24 } 25 26 struct line *creat() 27 { 28 struct line *p1,*p2; 29 int count; 30 printf("Please input the number of JieDian:"); 31 scanf("%d",&count); 32 p1 = p2 = (struct line *)malloc(sizeof(struct line)); 33 p1->num = 0; 34 head = p1; 35 for(int i = 0;i < count;i++) 36 { 37 p2 = (struct line *)malloc(sizeof(struct line)); 38 scanf("%d",&p2->num); 39 p1->next = p2; 40 p1 = p2; 41 } 42 p2->next = NULL; 43 return head; 44 } 45 void Change() 46 { 47 struct line *p,*q; 48 p = head->next; 49 head->next = NULL; 50 while(p != NULL) 51 { 52 q = p->next; 53 p->next = head->next; 54 head->next = p; 55 p = q; 56 } 57 } 58 void display() 59 { 60 struct line *p; 61 p = head->next; 62 if(p == NULL) 63 printf("NULL!"); 64 else 65 { 66 while(p != NULL) 67 { 68 printf("%d ",p->num); 69 p = p->next; 70 } 71 } 72 printf("\n"); 73 }
二.双向链表(做保留)
三.循环链表(做保留)