链表的创建,遍历,内存释放,检索,插入,删除,以及其他操作 (吉林大学 孙立鑫)

目录

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.小结 其实只要把链表中的元素放入数组中,又已知数组长度,我们就可以在很简单的情况下直接对数组进行操作,进而达到我们的目的,例如,交换节点,删除相同元素,删除对应元素,寻址结点,合并链表,排序.....数不胜数。但这是条件允许的情况下,在大多数情况下,我们只能对链表本身进行操作,以上内容就们什么用了。

本文中的代码均可按照题目要求进行修改,比如一开始就规定标志数字,不用每次都输入。

   总结,以上内容仅是我在学习过程中对链表知识的总结,难免会出现错误和不合理的地方,欢迎指出。希望本篇文章对你的生活和学习有所帮助。

 

 

 

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙立鑫 吉林大学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值