单链表基本操作(不带头结点)
链表概念:
一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一节点
具体操作概述
- 尾插一个元素
- 尾删一个元素
- 头插一个元素
- 头删一个元素
- 查找元素在链表中的位置,并且返回该节点的地址
- 在指定位置(pos)前插入元素
- 在指定位置(pos)后插入元素
- 删除指定位置元素
- 删除指定值的元素
- 指定值的所有元素都删除
- 求链表的元素个数
- 逆序打印单链表
不允许遍历链表,在指定位置前插入元素
首先创建一个结构体LinkNode,在结构体中定义一个结构体变量next来指向下一个节点,最后一个节点的next赋值NULL,用来作为结束标记,代码如下:
13 typedef struct LinkNode{
14 LinkNodeType data;
15 struct LinkNode* next;
16 }LinkNode;
对于数组的初始化我直接在主函数中定义一个结构体变量*head用来指向链表头部,定义时直接赋值为NULL
具体操作实现:
- 我们先写一个用来创建新节点的函数,用来进行插入操作,插入数据时直接将特定节点的next指向新节点,然后新节点的next指向原节点的下一节点就好
4 LinkNode* CreateNode(LinkNodeType value)//创建一个新节点
5 {
6 LinkNode* newNode=(LinkNode*)malloc(sizeof(LinkNode));
7 newNode->data = value;
8 newNode->next = NULL;
9 return newNode;
10 }
- 尾部插入一个数据
- 插入以及删除操作时,需要将指针*head的地址传过去,这样才可以通过形参来改变外部实参的指向,所以此类操作需要形参定义为二级指针
- 遍历找到链表的尾部,将尾部节点的next指向创建的新节点
11 void LinkListPushBack(LinkNode** head, LinkNodeType value)//尾插一个元素
12 {
13 if(head==NULL){
14 //非法输入
15 return;
16 }
17 //空链表
18 if(*head==NULL)
19 *head = CreateNode(value);
20 //非空链表
21 else{
22 LinkNode* cur = *head;
23 while(cur->next != NULL){
24 cur = cur->next;
25 }
26 cur->next = CreateNode(value);
27 return;
28 }
29 }
- 尾部删除一个数据
- 将尾部节点的前一节点next指向空,作为链表的结束,原尾部节点释放掉
32 void LinkListPopBack(LinkNode** head)//尾删一个元素
33 {
34 if(head==NULL){
35 return;
36 }
37 if(*head==NULL){
38 printf("空链表\n");
39 return;
40 }
41 else if((*head)->next==NULL){
42 free(*head);
43 *head==NULL;
44 }
45 else{
46 LinkNode* cur = *head;
47 LinkNode* end = NULL;
48 while(cur->next){
49 end = cur;
50 cur = cur->next;
51 }
52 free(cur);
53 end->next=NULL;
54 }
55 }
- 头插一个元素
- 将head指向一个新节点,新节点的next指向原链表头部
57 void LinkListPushTop(LinkNode** head, LinkNodeType value)//头插一个元素
58 {
59 if(head==NULL){
60 return;
61 }
62 LinkNode* pHead = CreateNode(value);
63 pHead->next = *head;
64 *head = pHead;
65 }
- 头删一个元素
- 将head指向其下一节点,释放原head指向的节点
67 void LinkListPopFront(LinkNode** head)//头删一个元素
68 {
69 if(head==NULL){
70 return;
71 }
72 if(*head==NULL){
73 printf("空链表\n");
74 return;
75 }
76 else if((*head)->next==NULL){//只有一个节点
77 free(*head);
78 *head==NULL;
79 }
80 else{
81 LinkNode* tmp = *head;
82 *head = (*head)->next;
83 free(tmp);
84 }
85 }
- 查找元素在链表中的位置,并且返回该节点的地址
- 如果该元素在链表中出现多次,只需返回第一个的地址
91 LinkNode* LinkListFind(LinkNode* head, LinkNodeType to_find)
92 {
93 if(head==NULL){
94 return;
95 }
96 LinkNode* cur = head;
97 while(cur){
98 if(cur->data == to_find){
99 return cur;
100 }
101 cur = cur->next;
102 }
103 return NULL;
104 }
- 在指定位置前插入数据
- 遍历链表,找到该位置的前一节点,标记为cur,cur的next指向新节点,新节点的next指向指定位置的节点
108 void LinkListInsert(LinkNode** head, LinkNode* pos, LinkNodeType value)
109 {
110 if(head==NULL){
111 return;
112 }
113 LinkNode* cur = *head;
114 if(pos==(*head)){
115 LinkListPushTop(head,value);
116 return;
117 }
118 while(cur->next!=pos){
119 cur = cur->next;
120 if(cur==NULL){
121 printf("Pos值输入错误\n");
122 return;
123 }
124 }
125 cur->next = CreateNode(value);
126 cur->next->next = pos;
127 }
- 在指定位置后插入数据
- 将指定位置的next指向新的节点,新节点的next指向指定位置的原下一个节点
130 void LinkListInsertAfter(LinkNode** head, LinkNode* pos, LinkNodeType value)
131 {
132 if(head==NULL){
133 return;
134 }
135 if(pos->next==NULL){
136 pos->next = CreateNode(value);
137 return;
138 }
139 LinkNode* tmp = *head;
140 while(tmp!=pos){
141 tmp = tmp->next;
142 if(tmp=NULL){
143 printf("pos值输入错误\n");
144 return;
145 }
146 }
147 pos = pos->next;
148 tmp->next = CreateNode(value);
149 tmp->next->next = pos;
150 }
删除指定位置的元素
将指定位置的前一节点的next指向指定位置节点的next,释放原指定位置节点
152 void LinkListErase(LinkNode** head, LinkNode* pos)
153 {
154 if(head==NULL){
155 return;
156 }
157 if(*head==NULL){
158 printf("空链表\n");
159 return;
160 }
161 if(pos==*head){
162 pos = *head;
163 *head = (*head)->next;
164 free(pos);
165 return;
166 }
167 LinkNode* cur = *head;
168 while(cur->next!=pos){
169 cur=cur->next;
170 }
171 cur->next = pos->next;
172 free(pos);
173 }
- 指定值的所有元素都删除
177 void LinkListRemove(LinkNode** head, LinkNodeType to_delete)
178 {
179 if(head==NULL){
180 return;
181 }
182 if(*head==NULL){
183 printf("空链表\n");
184 return;
185 }
186 LinkNode* del = *head;//定义del用来遍历链表
187 LinkNode* tmp = NULL;
188 while(del!=NULL){
189 if((*head)->data == to_delete){//如果链表头部元素即为指定值,则删除之后再改变头指针的位置
190 *head=(*head)->next;
191 del = *head;
192 }
193 else if(del->data==to_delete){
194 tmp->next = del->next;//此时的tmp为del前一节点的位置
195 }
196 else{
197 tmp = del;//一次没找到,则tmp指向del指向的节点
198 }
199 del=del->next;//del往后遍历
200 }
201 }
- 求链表的元素个数
203 size_t LinkListSize(LinkNode* head)
204 {
205 if(head==NULL){
206 return;
207 }
208 LinkNode* pos = head;
209 size_t count = 0;
210 while(pos!=NULL){
211 count++;
212 pos=pos->next;
213 }
214 return count;
215 }
- 逆序打印单链表
- 自己画的图片,估计我自己都要看不懂了,哈哈,凑合看吧
218 LinkNode* LinkListReversePrint(LinkNode* head)
219 {
220 if(head==NULL){
221 return;
222 }
223 LinkNode* next;
224 LinkNode* tmp = NULL;
225 while(head!=NULL){
226 next = head->next;
227 head->next=tmp;
228 tmp = head;
229 head = next;
230 }
231 return tmp;
232 }
- 不允许遍历链表,在指定位置前插入元素
- 在指定位置后插入新节点,将指定位置的值赋给新节点,指定位置的值再被插入的元素替换
235 void LinkListInsertBefore(LinkNode** head, LinkNode* pos, LinkNodeType value)
236 {
237 if(head==NULL){
238 return;
239 }
240 LinkNode* tmp = CreateNode(value);
241 tmp->data = pos->data;
242 tmp->next = pos->next;
243 pos->data = value;
244 pos->next = tmp;
245 }