目录
链表(Linked List)
链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的链接("links")
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。(插入去除都变得方便)但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
1.单向链表(Single-Linked List)
单链表是链表中结构最简单的。一个单链表的节点(Node)分为两个部分,第一个部分(data)保存或者显示关于节点的信息,另一个部分存储下一个节点的地址。最后一个节点存储地址的部分指向空值。
单向链表只可向一个方向遍历,一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。而插入一个节点,对于单向链表,我们只提供在链表头插入,只需要将当前插入的节点设置为头节点,next指向原头节点即可。删除一个节点,我们将该节点的上一个节点的next指向该节点的下一个节点。
1.创建链表-------》(1)创建节点(分配内存,填充数据域和指针域(NULL))
(2)将节点链接在一起
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
//节点结构体
struct node
{
unsigned char elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned char elem); //节点的创建
void insert_node(int pos, unsigned char elem); // 插入节点
void delete_node(int pos); //删除节点
void print_linklist(void); //输出链表
int search(unsigned char elem); //查询链表中的某个数值
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned char elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem; // 节点内容为elem
p->next = NULL; //刚开始节点指针为空
if(head == NULL)
head = p;
else
tail->next = p;
tail = p; // 更新尾节点
}
// 插入节点
void insert_node(int pos, unsigned char elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头节点
{
p->elem = elem;
p->next = head;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; //pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; //
p->next = pre->next;
pre->next = p;
if(p->next == NULL) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 头结点
{
head = head->next;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == NULL)
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(void)
{
struct node *p;
for(p = head; p; p = p->next)
printf("%c", p->elem);
}
//查询链表中的某个数值
int search(unsigned char elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
// 创建链表
create_list('A');
create_list('B');
create_list('Z');
create_list('X');
print_linklist();
printf("\n");
//删除
delete_node(0);
print_linklist();
printf("\n");
//插入
insert_node(2, 'Z');
insert_node(0, 'F');
print_linklist();
printf("\n");
//查询
if(search('A'))
printf("the elem is found\n");
else
printf("the elem is not found\n");
printf("\n");
return 0;
}
2.单链表应用-合并有序链表
链表 1 9 13 27
链表 3 5 14 81 88 95 99
合并 1 3 5 9 13 14 27 81 88 95 99
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; //main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); // 插入节点
void delete_node(int pos); //删除节点
void print_linklist(struct node *linklist_head); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p; // 更新尾节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; //pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; //
p->next = pre->next;
pre->next = p;
if(p->next == NULL) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == NULL) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(struct node *linklist_head) // 结构体型指针
{
struct node *p;
for(p = linklist_head; p; p = p->next)
printf("%5d", p->elem);
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
//两个链表的头指针
struct node *head1 = NULL;
struct node *head2 = NULL;
//结构体指针,用来遍历两个链表
struct node *p = NULL;
struct node *q = NULL;
// 创建链表1
create_list(1);
create_list(9);
create_list(13);
create_list(27);
head1 = head;
print_linklist(head1);
printf("\n");
// 创建链表2
head = NULL;
create_list(3);
create_list(5);
create_list(14);
create_list(81);
create_list(88);
create_list(95);
create_list(99);
head2 = head;
print_linklist(head2);
printf("\n");
//新链表
head = NULL;
p = head1;
q = head2;
while(p && q)
{
if(p->elem <= q->elem)
{
if(head == NULL) //head只更新一次
head = p;
else
tail->next = p;
tail = p;
p = p->next;
}
else
{
if(head == NULL) //head只更新一次
head = q;
else
tail->next = q;
tail = q;
q = q->next;
}
}
tail->next = p?p:q;
print_linklist(head);
printf("\n");
return 0;
}
3.单链表应用-删除链表重复元素
辅助数组flag[10]:下标:要检索的内容 元素:状态(是否出现过,0代表没出现过)
元素出现次数 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
数组下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; //main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); // 插入节点
void delete_node(int pos); //删除节点
void print_linklist(struct node *linklist_head); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p; // 更新尾节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; //pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; //
p->next = pre->next;
pre->next = p;
if(p->next == NULL) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == NULL) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(struct node *linklist_head) // 结构体型指针
{
struct node *p;
for(p = linklist_head; p; p = p->next)
printf("%5d", p->elem);
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
//删除重复元素
void delete_repeat(struct node *head)
{
int flag[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct node *p = head;
struct node *q = NULL;
flag[p->elem] = 1;
while(p->next != NULL)
{
if(flag[p->next->elem] == 0)
{
flag[p->next->elem] = 1;
p = p->next;
}
else
{
q = p->next;
p->next = q->next;
free(q);
}
}
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
// 创建链表1
create_list(1);
create_list(2);
create_list(3);
create_list(4);
create_list(5);
create_list(6);
print_linklist(head);
printf("\n");
delete_repeat(head);
print_linklist(head);
printf("\n");
return 0;
}
4.单链表应用-查找链表中间节点
快慢指针的使用:快指针走两步,慢指针走一步
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; //main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); // 插入节点
void delete_node(int pos); //删除节点
void print_linklist(struct node *linklist_head); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
void delete_repeat(struct node *head); //删除重复元素
int find_mid(struct node *head); //查找中间节点元素
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p; // 更新尾节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; //pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; //
p->next = pre->next;
pre->next = p;
if(p->next == NULL) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == NULL) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(struct node *linklist_head) // 结构体型指针
{
struct node *p;
for(p = linklist_head; p; p = p->next)
printf("%5d", p->elem);
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
//删除重复元素
void delete_repeat(struct node *head)
{
int flag[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct node *p = head;
struct node *q = NULL;
flag[p->elem] = 1;
while(p->next != NULL)
{
if(flag[p->next->elem] == 0)
{
flag[p->next->elem] = 1;
p = p->next;
}
else
{
q = p->next;
p->next = q->next;
free(q);
}
}
}
//查找中间节点
int find_mid(struct node *head)
{
struct node *p;
struct node *q;
p = q = head;
while(p != NULL && p->next != NULL)
{
p = p->next->next;
q = q->next;
}
return q->elem;
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
// 创建链表1
create_list(1);
create_list(2);
create_list(1);
create_list(4);
create_list(5);
create_list(4);
print_linklist(head);
printf("\n");
printf("mid = %d\n", find_mid(head));
return 0;
}
5.单链表应用-查找倒数第n个节点
快慢指针的使用:快指针先走n-1步,然后快慢指针同时一步一步的走
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; //main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); // 插入节点
void delete_node(int pos); //删除节点
void print_linklist(struct node *linklist_head); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
void delete_repeat(struct node *head); //删除重复元素
int find_mid(struct node *head); //查找中间节点元素
int find_last_nth(struct node *head, int n); //查找倒数第n个节点
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p; // 更新尾节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; //pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; //
p->next = pre->next;
pre->next = p;
if(p->next == NULL) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == NULL) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(struct node *linklist_head) // 结构体型指针
{
struct node *p;
for(p = linklist_head; p; p = p->next)
printf("%5d", p->elem);
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
//删除重复元素
void delete_repeat(struct node *head)
{
int flag[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct node *p = head;
struct node *q = NULL;
flag[p->elem] = 1;
while(p->next != NULL)
{
if(flag[p->next->elem] == 0)
{
flag[p->next->elem] = 1;
p = p->next;
}
else
{
q = p->next;
p->next = q->next;
free(q);
}
}
}
//查找中间节点
int find_mid(struct node *head)
{
struct node *p;
struct node *q;
p = q = head;
while(p != NULL && p->next != NULL)
{
p = p->next->next;
q = q->next;
}
return q->elem;
}
//查找倒数第n个节点
int find_last_nth(struct node *head, int n)
{
int i;
struct node *p;
struct node *q;
p = q = head;
for(i = 0; i < n-1; i++)
p = p->next;
while(p->next != NULL)
{
p = p->next;
q = q->next;
}
return q->elem;
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
int n;
// 创建链表1
create_list(1);
create_list(2);
create_list(3);
create_list(4);
create_list(5);
create_list(6);
create_list(7);
create_list(8);
create_list(9);
create_list(10);
create_list(11);
create_list(12);
printf("please enter the last one you want to show:");
scanf("%d", &n);
printf("the last n:%d\n", find_last_nth(head, n));
printf("\n");
return 0;
}
6.单链表应用-逆置单链表
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; //main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); // 插入节点
void delete_node(int pos); //删除节点
void print_linklist(struct node *linklist_head); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
void delete_repeat(struct node *head); //删除重复元素
int find_mid(struct node *head); //查找中间节点元素
int find_last_nth(struct node *head, int n); //查找倒数第n个节点
void reverse_linklist(struct node *linklist_head); //逆置链表
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p; // 更新尾节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; //pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; //
p->next = pre->next;
pre->next = p;
if(p->next == NULL) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == NULL) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(struct node *linklist_head) // 结构体型指针
{
struct node *p;
for(p = linklist_head; p; p = p->next)
printf("%5d", p->elem);
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
//删除重复元素
void delete_repeat(struct node *head)
{
int flag[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct node *p = head;
struct node *q = NULL;
flag[p->elem] = 1;
while(p->next != NULL)
{
if(flag[p->next->elem] == 0)
{
flag[p->next->elem] = 1;
p = p->next;
}
else
{
q = p->next;
p->next = q->next;
free(q);
}
}
}
//查找中间节点
int find_mid(struct node *head)
{
struct node *p;
struct node *q;
p = q = head;
while(p != NULL && p->next != NULL)
{
p = p->next->next;
q = q->next;
}
return q->elem;
}
//查找倒数第n个节点
int find_last_nth(struct node *head, int n)
{
int i;
struct node *p; //快指针
struct node *q; //慢指针
p = q = head;
for(i = 0; i < n-1; i++) //快指针先走n-1步
p = p->next;
while(p->next != NULL) //只要p->next不为空,p q 就一直一起走
{
p = p->next;
q = q->next;
}
return q->elem;
}
// 逆置链表
void reverse_linklist(struct node *linklist_head)
{
struct node *p, *n;
p = linklist_head->next;
linklist_head->next = NULL;
while(p->next != NULL)
{
n = p->next;
p->next = linklist_head;
linklist_head = p;
p = n;
}
p->next = linklist_head;
linklist_head = p;
head = linklist_head; //给全局变量特别重要,要不无法正常输出
//因为传入的实参head始终指向的原来的头,变得是形参
//linklist_head
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
int n;
// 创建链表1
create_list(1);
create_list(2);
create_list(3);
create_list(4);
create_list(5);
create_list(6);
create_list(7);
create_list(8);
create_list(9);
create_list(10);
create_list(11);
create_list(12);
reverse_linklist(head); //逆转
print_linklist(head); //输出显示,注意这里在Linklist.c的操作
printf("\n");
return 0;
}