链表
数组和链表对比:
数组是顺序存储,空间有限,不好变化。而链表是链式存储,空间无限,且删除增长方便。但由于链表查找数据需要遍历,所以数组效率比链表高
单链表
结点:数据域和指针域
N个结点通过指针域相互链接组成一个链表
链表中第一个元素所在的结点称为首元结点,首元结点前还有一个结点,称为头结点(不是必须有的,但必须有头指针)。头结点数据域为空,头指针指向头结点或首元结点的数据域,如果头结点指针域为NULL,则表明此链表为空表
概念:开始有头指针,数据元素+指向下一个结点元素的指针,直到结尾指针指向NULL结束(没有指向的空间)
定义一个单链表结点类型:
//定义结点类型
typedef struct Node {
int data; //数据域。数据类型,你可以把int型的data换成任意数据类型,包括结构体struct等复合类型
struct Node *next; //单链表的指针域
} Node,*LinkedList;
//Node表示结点的类型,LinkedList表示指向Node结点类型的指针类型,一般用于返回链表的函数(链表就是结点的指针)
初始化:
LinkedList listinit(){
Node *L; //结点指针为链表
L=(Node*)malloc(sizeof(Node)); //开辟空间
if(L==NULL){ //判断是否开辟空间失败,这一步很有必要
printf("申请空间失败");
//exit(0); //开辟空间失败可以考虑直接结束程序
}
L->next=NULL; //指针指向空
return L;
}
//就可以生成一个空链表
头插法和尾插法
头插法:插入到头结点后面
//单链表的建立1,头插法建立单链表
LinkedList LinkedListCreatH() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申请头结点空间
L->next = NULL; //初始化一个空链表
int x; //x为链表数据域中的数据
scanf("%d",&x);
while(x != 9999) {
Node *p;
p = (Node *)malloc(sizeof(Node)); //申请新的结点
p->data = x; //结点数据域赋值
p->next = L->next; //头结点指针域赋值给新结点指针
L->next = p; //头结点指针指向整个新节点(开头是数据域)
scanf("%d",&x);
}
return L;
}
图示过程:
尾插法:插入到表尾后面
先建立一个空单链表
尾指针r指向头结点
- 生成一个新结点§
- 读入数据到p
- 将新结点插入到r结点之后 r->next = p;
- r指向新的尾结点 r=p;
//单链表的建立2,尾插法建立单链表
LinkedList LinkedListCreatT() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申请头结点空间
L->next = NULL; //初始化一个空链表
Node *r;
r = L; //r始终指向终端结点,开始时指向头结点
int x; //x为链表数据域中的数据
scanf("%d",&x);
while(x != 9999)
{
Node *p;
p = (Node *)malloc(sizeof(Node)); //申请新的结点
p->data = x; //结点数据域赋值
r->next = p; //将结点插入到表尾后
r = p; //r指向新结点
scanf("%d",&x);
}
r->next = NULL;
return L;
}
遍历
思路就是建立一个指向链表L的结点,然后沿着逐个向后搜索即可
//便利输出单链表
void printList(LinkedList L){
Node *p=L->next;
int i=0;
while(p){
printf("第%d个元素的值为:%d\n",++i,p->data);//L->next->data(下一个结点数据)
p=p->next; //p = L -> next -> next ...
}
}
修改
//链表内容的修改,再链表中修改值为x的元素变为为k。 建立新结点遍历到更改的位置就行
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
Node *p=L->next;
int i=0;
while(p){
if(p->data==x){
p->data=k;
}
p=p->next;
}
if (p == NULL)
{
printf("未找到%d,请确认后再修改!\n",x);
exit(0);
}
return L;
}
插入
循环查找到前驱结点(前一个),再插入
//单链表的插入,在链表的第i个位置插入x的元素
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
Node *pre; //pre为前驱结点
pre = L;
for (int tempi = 1; tempi < i; tempi++) {
pre = pre->next; //查找第i个位置的前驱结点
}
Node *p; //插入的结点为p
p = (Node *)malloc(sizeof(Node));
p->data = x;
p->next = pre->next; //第i个位置给新结点指针
pre->next = p; //前驱结点后面插入该新结点
return L;
}
删除
建立前驱结点和当前结点,当要删除数据时,直接将前驱结点跳过删除结点指向删除结点后一个结点,再将原有结点释放
//单链表的删除,在链表中删除值为x的元素
LinkedList LinkedListDelete(LinkedList L,int x) {
Node *p,*pre; //pre为前驱结点,p为查找的结点。
p = L->next; //头指针赋给p
while(p->data != x) { //查找值为x的元素
pre = p;
p = p->next;
}
pre->next = p->next; //删除操作,将其前驱next指向其后继。
free(p);
return L;
}
释放链表内存
void LinkListFree(LinkList L) {
Node* p = L->next;
while (p) {
Node* pNext = p->next; //缓存后一个结点,再删除原位置结点
free(p);
p = pNext;
}
free(L);
}
完整代码:
while(scanf("%d",&x) != EOF) //while(1){if(scanf("%d",&x) != EOF){}} 一直死循环 不管写入成功与否
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
//定义结点类型
typedef struct Node {
int data; //数据类型,你可以把int型的data换成任意数据类型,包括结构体struct等复合类型
struct Node *next; //单链表的指针域
} Node,*LinkedList;
//单链表的初始化
LinkedList LinkedListInit() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申请结点空间
if(L==NULL){ //判断申请空间是否失败
exit(0); //如果失败则退出程序
}
L->next = NULL; //将next设置为NULL,初始长度为0的单链表
return L;
}
//单链表的建立1,头插法建立单链表
LinkedList LinkedListCreatH() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申请头结点空间
L->next = NULL; //初始化一个空链表
int x; //x为链表数据域中的数据
scanf("%d",&x);
while (x != 9999) {
Node *p;
p = (Node *)malloc(sizeof(Node)); //申请新的结点
p->data = x; //结点数据域赋值
p->next = L->next; //头结点指针域赋值给新结点指针
L->next = p; //头结点指针指向整个新节点(开头是数据域)
scanf("%d", &x);
}
return L;
}
LinkedList LinkedListCreatT() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申请头结点空间
L->next = NULL; //初始化一个空链表
Node *r;
r = L; //r始终指向终端结点,开始时指向头结点
int x; //x为链表数据域中的数据
scanf("%d",&x);
while(x != 9999)
{
Node *p;
p = (Node *)malloc(sizeof(Node)); //申请新的结点
p->data = x; //结点数据域赋值
r->next = p; //将结点插入到表头L-->|1|-->|2|-->NULL
r = p;
scanf("%d",&x);
}
r->next = NULL;
return L;
}
//单链表的插入,在链表的第i个位置插入x的元素
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
Node *pre; //pre为前驱结点
pre = L;
int tempi = 0;
for (tempi = 1; tempi < i; tempi++) {
pre = pre->next; //查找第i个位置的前驱结点
}
Node *p; //插入的结点为p
p = (Node *)malloc(sizeof(Node));
p->data = x;
p->next = pre->next;
pre->next = p;
return L;
}
//单链表的删除,在链表中删除值为x的元素
LinkedList LinkedListDelete(LinkedList L,int x) {
Node *p,*pre; //pre为前驱结点,p为查找的结点。
p = L->next;
while(p->data != x) { //查找值为x的元素
pre = p;
p = p->next;
}
pre->next = p->next; //删除操作,将其前驱next指向其后继。
free(p);
return L;
}
//链表内容的修改,再链表中修改值为x的元素变为为k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
Node *p=L->next;
int i=0;
while(p){
if(p->data==x){
p->data=k;
}
p=p->next;
}
return L;
}
//建立新结点遍历输出单链表
void printList(LinkedList L){
Node *p=L->next;
int i=0;
while(p){
printf("第%d个元素的值为:%d\n",++i,p->data);
p=p->next;
}
}
int main() {
//创建
LinkedList list;
printf("请输入单链表的数据:以9999结尾\n");
list = LinkedListCreatT();
//list=LinkedListCreatT();
printList(list);
//插入
int i;
int x;
printf("请输入插入数据的位置:");
scanf("%d",&i);
printf("请输入插入数据的值:");
scanf("%d",&x);
LinkedListInsert(list,i,x);
printList(list);
//修改
printf("请输入修改的数据:");
scanf("%d",&i);
printf("请输入修改后的值:");
scanf("%d",&x);
LinkedListReplace(list,i,x);
printList(list);
//删除
printf("请输入要删除的元素的值:");
scanf("%d",&x);
LinkedListDelete(list,x);
printList(list);
return 0;
}