目录
1.创建单向链表
a.头尾指针均含有
b.只有头指针(较为常见)
2.遍历链表
a.包含最后一个元素
b.不包含最后一个元素
3.内存释放
(哨兵的介绍)
4.检索
5.插入
6.删除
7.修改
(替换)
8.其他操作
a.删除相同元素
b.寻址中间节点
c.替换对应节点
d.链表排序
e.链表合并
进阶操作:***********************************************************************************
1.数组存放链表元素(考试一般用不到)
a.删除相同元素
b.寻址中间节点
c.交换对应节点
d.小结
1.创建单向链表
a.头尾指针均含有
谈到链表,我们需要知道,链表是一种线性表,它由若干节点组成,相邻节点之间又通过指针相互连接。
节点,即为包含数据和结构体指针的结构体。例:
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
(typedef的使用可有可无) 若使用,lb则为声明结构体节点的标识符。
若干相邻节点,则通过struct lx* next;这串代码实现链接。
那么,首元节点如何创建呢?
我们假设使用了如下的头文件和全局变量:
#include<stdio.h>
#include<stdlib.h>
typedef struct lx
{
int data;
struct lx* next;
}lb;
lb* top; //声明全局变量 头指针
lb* rear; //尾指针
那么 我们将通过如下函数创建首元节点
void shouyuan(int i) //首元节点 包含全局变量 (头指针 尾指针)
{
lb* a;
a = (lb*)malloc(sizeof(lb));
if (a)//防止编译器认为a指向空而报错
{
a->next = NULL;
a->data = i;
}
top = a; //头尾均指向首元节点
rear = a;
}
那么现在我们有了第一个节点,首元节点。
我们需要在这条链上添加元素,那么我们如何添加元素呢,我们有两种添加方式,分别是在链表头添加,和在链表尾添加。
从链表头添加:
void pusht(int i)
{
lb* a;
a = (lb*)malloc(sizeof(lb));
if (a)
{
a->data = i;
a->next = top; //指向原来的头节点
}
top = a; //头指针指向现在头节点
}
从链表尾添加:
void pushr(int i)
{
lb* a;
a = (lb*)malloc(sizeof(lb));
if (a)
{
a->data = i;
a->next = NULL; //空指针
}
rear->next = a; //原来尾结点指向现在尾节点
rear = rear->next; //尾指针指向现在尾结点
}
通过以上三个函数,我们便能成功创建任意长度的链表。
b.只含有头指针(顺次添加元素)
其实,若沿用上述函数,二者区别不大,无非是删掉所有关于尾指针的操作
但是,较为常见的形式,是让我们输入一串以某某数字结尾的数字串,然后进行后续操作,那么我们又该如何操作呢?
我们让某某数字为全局变量
头文件和全局变量如下:
我们假设使用了如下的头文件和全局变量:
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
创建链表的函数如下:(和上述有较大不同)
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->data的值,防止报错
}
} while (test != wrong);
return top;
}
在该函数中,我们输入链表的结束标志,以及以结束标志为结尾的一串整型数字。
它的返回值,是我们创建链表的首元节点的地址,也就是头指针。
2.遍历链表
说到遍历链表,我们需要知道的是它的头指针。
那么便出现了两种遍历形式。
a.包含最后一个元素
b.不包含最后一个元素(最后一个元素为结束标志)
a.包含最后一个元素(较为简单)
void display(lb* x)
{
while (x) //非空进循环 完成遍历链表的操作
{
printf("%d ", x->data);
x = x->next;
}
}
若要求相邻元素以一个空格相隔,我们需要这样写代码。
void display(lb* x)
{
while (x) //非空进循环 完成遍历链表的操作
{
if(x->next==NULL)
{
printf("%d",x->data);
x=x->next;
}
else
{
printf("%d ", x->data);
x = x->next;
}
}
}
b.不包含最后一个元素(相邻元素以一个空格间隔)
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
3.内存释放
要想释放内存,我们需要知道链表的头指针。
释放函数如下
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
写到这里,我们来对以上内容进行一个整合。
例题:输入一个结束字符 ,然后输入一串以结束字符结束的一串数字。然后输出该串数字(无结束字符),每个数字之间以一个空格间隔,最后一个数字后无空格。
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
display(top);
release(top);
return 0;
}
若输入 12 1 5 3 2 4 7 8 9 12
输出如下
1 5 3 2 4 7 8 9
但是,如果输入的数字串第一个就是结束字符,比较刁钻,我们需要对代码进行一定修改,具体为两处。
第一处:在创建列表的函数中,用如下代码段替代旧代码段
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
第二处:
更改主函数
int main(void)
{
lb* top=build();
if(top==NULL)
{
printf("NULL");
return 0;
}
display(top);
release(top);
return 0;
}
更改后的代码如下(适应刁钻情况)
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
if(top==NULL)
{
printf("NULL");
return 0;
}
display(top);
release(top);
return 0;
}
如果我们输入12 12 5 6 4 7 12 输出如下
NULL
(哨兵介绍)
何为哨兵?哨兵是一个特殊的首元节点,作为头节点,它的值域无值,但指向下一个节点。
这样一来,链表的第二个结点是链表的第一个元素。它能够使链表更加有边界感,便于进行更多操作。
我们来重写带有哨兵的创建链表函数。
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
遍历链表的函数不用重写
但用法从display(top)变成了display(top->next)
release函数的写法和用法都不用变 依旧是release(top) 因为哨兵节点也要释放。
主函数中的判断语句要发生变化
从if(top==NULL) 变成if(top->next==NULL)
修改后成品如下:
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
if(top->next==NULL)
{
printf("NULL");
return 0;
}
display(top->next);
release(top);
return 0;
}
我们输入 0 1 2 3 4 5 6 0
输出
1 2 3 4 5 6
输入
0 0 1 2 3 4 5 6 0
输出
NULL
4.检索
我们有了哨兵节点的帮助,自然能够更轻松地实现检索,什么是一个节点的特殊标志呢,那就是这个节点值域中的值。但更重要的是,我们需要知道它的前驱结点,有了前驱节点,我们便能任意进行插入,删除,修改的操作。
我们输入哨兵节点(top)和想要检索的值,返回一个该值对应节点的前驱节点。
若寻不到,返回值就是空。但这样检索得到的只是链表中第一次出现这个值的结点的前驱节点。
lb* search(lb*top,int i)
{
lb* x=top;
while(x&&x->next)
{
if((x->next)->data==i)
{
return x;
}
x=x->next;
}
return NULL;
}
5.插入
若想在对应位置插入一个值,需要已知前驱节点。
我们输入前驱结点和想插入的值,即可完成插入。函数如下
void insert (lb* x,int i)
{
lb*p;
p = (lb*)malloc(sizeof(lb));
if(p)
{
p->data=i;
p->next=x->next;
x->next=p;
}
}
6.删除
要想删除对应位置结点,我们需要知道它的前驱节点。
函数如下
void dele (lb* x)
{
lb* temp;
temp=x->next;
x->next=(x->next)->next;
free(temp);
}
7.修改
要想修改对应位置结点,我们需要知道它的前驱节点,以及改动后的值。
函数如下
void reform(lb* x,int i)
{
(x->next)->data=i;
}
(替换)
先介绍求对应元素前驱节点的函数
lb* callc(int i,lb* top)
{
int j=0;
lb* p;
p=top;
for(;j<i-1;j++)
{
p=p->next;
}
return p;
}
输入两个位置的对应前驱结点
void replace(lb* x,lb* y)
{
if(y!=x->next)
{
lb* temp1;
lb* temp2;
lb* temp11;
lb* temp22;
temp1=x->next;
temp11=(x->next)->next;
temp2=y->next;
temp22=(y->next)->next;
(x->next)->next=NULL;
(y->next)->next=NULL;
x->next=NULL;
x->next=NULL;
x->next=temp2;
temp2->next=temp11;
y->next=temp1;
temp1->next=temp22;
}
else
{
lb*tem;
tem=(y->next)->next;
x->next=y->next;
(x->next)->next=y;
y->next=tem;
}
}
8.其他操作
有了以上插入,删除,修改的操作基础,我们便能够进行其他操作。
a.删除重复结点
很明显,我们需要用到上述的检索和删除函数。(我们的链表必须含有哨兵)
那么 删除函数怎么写?(注意,此时的链表元素无非两种情况)
一种是以一个标志数字结尾的数字串
另一种是第一个就是标志数字,结尾也是标志数字的数字串(函数不运行程序就退出)
所以,写函数时只考虑第一个数字不是标志数字的数字串(类似于display函数)
在主函数中,我们写成del_same(top) 括号内填上首元节点(哨兵节点)
函数如下
void del_same(lb* x)
{
lb* p=x;
while(p&&p->next)
{
lb* q=p->next;
int i=(p->next)->data;
while(q&&q->next)
{
if(search(q,i))
{
dele(search(q,i));
}
else
{
q=q->next;
}
}
p=p->next;
}
}
完整源代码
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
lb* search(lb*top,int i)
{
lb* x=top;
while(x&&x->next)
{
if((x->next)->data==i)
{
return x;
}
x=x->next;
}
return NULL;
}
void dele (lb* x)
{
lb* temp;
temp=x->next;
x->next=(x->next)->next;
free(temp);
}
void del_same(lb* x)
{
lb* p=x;
while(p&&p->next)
{
lb* q=p->next;
int i=(p->next)->data;
while(q&&q->next)
{
if(search(q,i))
{
dele(search(q,i));
}
else
{
q=q->next;
}
}
p=p->next;
}
}
int main(void)
{
lb* top=build();
if(top->next==NULL)
{
printf("NULL");
return 0;
}
del_same(top);
display(top->next);
release(top);
return 0;
}
我们输入 0 4 4 4 4 4 0 输出
4
我们输入 0 0 4 4 4 0 输出
NULL
我们输入 0 4 2 1 3 3 2 0
输出
4 2 1 3
b.求链表中间节点
若中间结点有两个,则设定前一个为中间位置结点;
我们需要知道,这里的(链表)是去掉了哨兵节点和尾结点(标志数字)
我们只要算出元素个数,就可以找到中间元素的前驱结点。
算出元素个数
int calcu(lb* x)
{
int sum=0;
while(x)
{
if((x->next)->data==wrong)
{
sum++;
return;
}
else
{
sum++;
x=x->next;
}
}
}
算出中间元素的前驱结点
lb* mid(lb* x,int i)
{
int j=0;
lb*p;
p=x;
if(i%2!=0)
{
for(;j<(i+1)/2-1;j++)
{
p=p->next;
return p;
}
}
else
{
for(;j<i/2-1;j++)
{
p=p->next;
return p;
}
}
}
源代码如下
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
int calcu(lb* x)
{
int sum=0;
while(x)
{
if((x->next)->data==wrong)
{
sum++;
return sum;
}
else
{
sum++;
x=x->next;
}
}
}
lb* mid(lb* x,int i)
{
int j=0;
lb*p;
p=x;
if(i%2!=0)
{
for(;j<(i+1)/2-1;j++)
{
p=p->next;
}
return p;
}
else
{
for(;j<i/2-1;j++)
{
p=p->next;
}
return p;
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
if(top->next==NULL)
{
printf("NULL");
return 0;
}
int i;
lb* lx;
i=calcu(top->next);
lx=mid(top,i);
display(lx->next);
release(top);
return 0;
}
输入 0 5 4 2 1 3 0
输出
2 1 3
输入 0 4 2 1 3 3 2 0
输出
1 3 3 2
c.替换对应节点
很明显,我们需要使用求对应节点前驱节点函数以及替换函数。
源代码如下
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
lb* callc(int i,lb* top)
{
int j=0;
lb* p;
p=top;
for(;j<i-1;j++)
{
p=p->next;
}
return p;
}
void replace(lb* x,lb* y)
{
if(y!=x->next)
{
lb* temp1;
lb* temp2;
lb* temp11;
lb* temp22;
temp1=x->next;
temp11=(x->next)->next;
temp2=y->next;
temp22=(y->next)->next;
(x->next)->next=NULL;
(y->next)->next=NULL;
x->next=NULL;
x->next=NULL;
x->next=temp2;
temp2->next=temp11;
y->next=temp1;
temp1->next=temp22;
}
else
{
lb*tem;
tem=(y->next)->next;
x->next=y->next;
(x->next)->next=y;
y->next=tem;
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
if(top->next==NULL)
{
printf("NULL");
return 0;
}
int x,y;
scanf("%d %d",&x,&y);
lb* xx;
lb* yy;
xx=callc(x,top);
yy=callc(y,top);
replace(xx,yy);
display(top->next);
release(top);
return 0;
}
我们输入0 1 2 3 4 5 6 0 1 5
输出
5 2 3 4 1 6
输入0 0 1 2 3 4 5 6 0 1 5
输出
NULL
d.链表排序
我们需要排序函数
在编写排序函数时,我们同样需要交换节点的函数。
假设我们已经有了交换节点的函数
排序函数如下
void paixu(lb* top)
{
lb* x;
int flag=1;
while(flag)
{
flag=0;
x=top;
while(x&&x->next&&(x->next)->next&&((x->next)->next)->next)
{
if((x->next)->data>((x->next)->next)->data)
{
flag=1;
replace(x,x->next);
x=x->next;
}
x=x->next;
}
}
}
源代码如下
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
void replace(lb* x,lb* y)
{
if(y!=x->next)
{
lb* temp1;
lb* temp2;
lb* temp11;
lb* temp22;
temp1=x->next;
temp11=(x->next)->next;
temp2=y->next;
temp22=(y->next)->next;
(x->next)->next=NULL;
(y->next)->next=NULL;
x->next=NULL;
x->next=NULL;
x->next=temp2;
temp2->next=temp11;
y->next=temp1;
temp1->next=temp22;
}
else
{
lb*tem;
tem=(y->next)->next;
x->next=y->next;
(x->next)->next=y;
y->next=tem;
}
}
void paixu(lb* top)
{
lb* x;
int flag=1;
while(flag)
{
flag=0;
x=top;
while(x&&x->next&&(x->next)->next&&((x->next)->next)->next)
{
if((x->next)->data>((x->next)->next)->data)
{
flag=1;
replace(x,x->next);
x=x->next;
}
x=x->next;
}
}
}
int main(void)
{
lb* top=build();
if(top->next==NULL)
{
printf("NULL");
return 0;
}
paixu(top);
display(top->next);
release(top);
return 0;
}
输入0 2 3 6 4 5 0
输出
2 3 4 5 6
输入0 0 2 3 4 0
输出
NULL
e.合并单链表
我们需要知道两个链表的头指针,决定哪个链表排在前。
然后,通过合并函数,将其进行合并。
合并函数如下
void hebing(lb* top1,lb* top2)
{
lb* p=top1;
while((p->next)->data!=wrong)
{
p=p->next;
}
free(p->next);
p->next=top2->next;
free(top2);
}
源代码如下
#include<stdio.h>
#include<stdlib.h>
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));//哨兵结点
top=temp=p;
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top->next = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top->next = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
printf("%d",x->data);
return;
}
else
{
printf("%d ",x->data);
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
void hebing(lb* top1,lb* top2)
{
lb* p=top1;
while((p->next)->data!=wrong)
{
p=p->next;
}
free(p->next);
p->next=top2->next;
free(top2);
}
int main(void)
{
lb* top1=build();
if(top1->next==NULL)
{
printf("NULL");
return 0;
}
lb* top2=build();
hebing(top1,top2);
display(top1->next);
release(top1);
return 0;
}
输入 0 1 2 3 4 0 0 5 6 7 8 0
输出
1 2 3 4 5 6 7 8
输入 0 0 1 2 3 4 0 0 5 6 7 8 0
输出NULL
进阶操作:*********************************************************************************************
1.数组存放链表元素(考试一般用不到)
对链表中的元素进行操作,我们可以先将链表中的元素都存放到一个数组中,然后对数据进行操作即可。(考试时大概率会强制对链表进行操作,所以可能用不到)。
我们只需要在上述代码的基础上,这样操作即可。
添加如下全局变量(数组长度酌情即可)
int a[100];
int sum=0;//计数器
然后再display函数进行如下修改
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
a[sum]=x->data;
sum++;
return;
}
else
{
a[sum]=x->data;
sum++;
x=x->next;
}
}
}
这样 sum的值就是数组元素总个数,也是链表元素总个数,且链表中的所有元素都从头到尾存放在了数组中。
在display函数中取消printf操作,然后在主函数中,release函数后,对数组进行操作,然后循环输出数组元素。
这样就大大简化了操作难度。
我们可以再次添加全局变量
int b[100];
int sum1=0;
用于存放操作后的元素
在这里介绍几个函数
a.删除重复元素
void del(int x[],int y)
{
int u = 0;
for (; u < y - 1; u++)
{
if (x[u] == 15987)
{
;
}
else
{
int k = u + 1;
for (; k < y; k++)
{
if (x[u] == x[k])
{
x[k] = 15987;
}
}
}
}
int s = 0;
for (; s < sum; s++)
{
if (x[s] == 15987)
{
;
}
else
{
b[sum1] = a[s];
sum1++;
}
}
}
我们试着运行 输入结尾标志数字 以及以该数字结尾的一串数字
以下为源代码
#include<stdio.h>
#include<stdlib.h>
int a[100];
int sum=0;
int b[100];
int sum1=0;
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
a[sum]=x->data;
sum++;
return;
}
else
{
a[sum]=x->data;
sum++;
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
ff=x;
x=x->next;
free(ff);
}
}
void del(int x[],int y)
{
int u = 0;
for (; u < y - 1; u++)
{
if (x[u] == 15987)
{
;
}
else
{
int k = u + 1;
for (; k < y; k++)
{
if (x[u] == x[k])
{
x[k] = 15987;
}
}
}
}
int s = 0;
for (; s < sum; s++)
{
if (x[s] == 15987)
{
;
}
else
{
b[sum1] = a[s];
sum1++;
}
}
}
int main(void)
{
lb* top=build();
if(top==NULL)
{
printf("NULL");
return 0;
}
display(top);
release(top);
del(a,sum);
int i=0;
for(;i<sum1;i++)
{
if(i==sum1-1)
{
printf("%d",b[i]);
}
else
{
printf("%d ",b[i]);
}
}
return 0;
}
如 5 11 33 64 89 47 33 11 64 89 47 5
输出如下
11 33 64 89 47
以下这句话很重要:
注意:上述代码没有哨兵,若有哨兵,应对主函数进行修改
if(top==NULL)改成if(top->next==NULLL) display(top)改成display(top->next)
(别忘了修改创建链表的函数)
b.寻址中间节点
若中间结点有两个,则设定前一个为中间位置结点;
输出包括结点在内的之后所有元素
我们连存放操作后的元素的b[100] sum1 都不需要。在把元素送入a[100]中后,在主函数的输出中进行操作即可。
#include<stdio.h>
#include<stdlib.h>
int a[100];
int sum=0;
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
a[sum]=x->data;
sum++;
return;
}
else
{
a[sum]=x->data;
sum++;
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
lb* ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
if(top==NULL)
{
printf("NULL");
return 0;
}
display(top);
release(top);
if(sum%2==0)
{
int f=sum/2-1;
for(;f<sum;f++)
{
if(f==sum-1)
{
printf("%d",a[f]);
}
else
{
printf("%d ",a[f]);
}
}
}
else
{
int f=(sum+1)/2-1;
for(;f<sum;f++)
{
if(f==sum-1)
{
printf("%d",a[f]);
}
else
{
printf("%d ",a[f]);
}
}
}
return 0;
}
我们输入 0 5 4 2 1 3 0
输出
2 1 3
我们输入
0 4 2 1 3 3 2 0
输出
1 3 3 2
c.交换结点
其实也是很简单的操作 输入结束标志数字 以结束标志结束的一串数字 交换的位置
代码如下
和上面代码相比 我们只更改主函数
源代码如下
#include<stdio.h>
#include<stdlib.h>
int a[100];
int sum=0;
int wrong;//链表结束标志
typedef struct lx
{
int data;
struct lx* next;
}lb;//链表结点
lb* build(void)
{
int test=0;//中间变量(下文有应用)
lb* top;//链表头指针
lb* temp;//移位指针
lb* p;//用于存储申请的空间的地址
top = temp = p = NULL;//良好的编程习惯
scanf("%d", &wrong);//链表结束标志
p = (lb*)malloc(sizeof(lb));
int i;//输入第一个数据
scanf("%d", &i);
if (p)//防止系统认为p可能指向空而报错
{
if (i == wrong)
{
top = NULL;
temp = p;
p->data = i;
p->next = NULL;
}
else
{
top = p;
temp = p;
p->data = i;
p->next = NULL;
}
}
do
{
int j;
scanf("%d", &j);
p = (lb*)malloc(sizeof(lb));
if (p && temp)//防止系统认为p可能指向空而报错
{
p->data = j;
temp->next = p;
temp = p;
temp->next = NULL;
test = temp->data;//中间变量存储temp->next的值,防止报错
}
} while (test != wrong);
return top;
}
void display(lb* x)
{
while(x)
{
if((x->next)->data==wrong)
{
a[sum]=x->data;
sum++;
return;
}
else
{
a[sum]=x->data;
sum++;
x=x->next;
}
}
}
void release(lb* x)
{
lb* ff;
while(x)
{
lb* ff=x;
x=x->next;
free(ff);
}
}
int main(void)
{
lb* top=build();
if(top==NULL)
{
printf("NULL");
return 0;
}
display(top);
release(top);
int tt;
int xx;
int ttt;
int xxx;
scanf("%d %d",&tt,&xx);
ttt=a[tt-1];
xxx=a[xx-1];
a[tt-1]=xxx;
a[xx-1]=ttt;
int i=0;
for(;i<sum;i++)
{
if(i==sum-1)
{
printf("%d",a[i]);
}
else
{
printf("%d ",a[i]);
}
}
return 0;
}
输入 0 1 2 3 4 5 6 0 1 5
输出
5 2 3 4 1 6
输入0 0 1 2 3 4 5 6 0 1 5
输出
NULL
d.小结 其实只要把链表中的元素放入数组中,又已知数组长度,我们就可以在很简单的情况下直接对数组进行操作,进而达到我们的目的,例如,交换节点,删除相同元素,删除对应元素,寻址结点,合并链表,排序.....数不胜数。但这是条件允许的情况下,在大多数情况下,我们只能对链表本身进行操作,以上内容就们什么用了。
本文中的代码均可按照题目要求进行修改,比如一开始就规定标志数字,不用每次都输入。
总结,以上内容仅是我在学习过程中对链表知识的总结,难免会出现错误和不合理的地方,欢迎指出。希望本篇文章对你的生活和学习有所帮助。