2.C_数据结构_线性表

线性表的描述

线性表就是若干数据的一个线性序列。

数学表达式:

L:表名

a0~an-1:数据元素

n:表长,n>0是为非空表

二元描述形式:

D:数据元素D用 ai 表示,这个 i 范围是0~n-1

R:关系用R表示,这个关系是<ai,ai+1>,表示ai为ai+1的直接前驱,ai+1为ai的直接后继。

<xxx1,xxx2>:这符号称为有序对,xxx1是xxx2的直接前驱,xxx2是xxx1的直接后继。

                        xxx1和xxx2可以为一个或多个。

                        当xxx1与xxx2都为1个时,这代表是1对1的关系,是逻辑结构中的线性结构

二元描述形式示例:

D代表数据元素,在这里就是1,2,3,4,5,6

R代表关系,用<xxx1,xxx2>表示各个元素之间的前驱与后继关系。

从图中可以看到1是2的前驱,2是1的后驱。所以<1,2>。后面的<2,3>....都同理。

线性表的特征:

  • 对于非空表,表头没前驱、表尾没后驱
  • 对于非空表,其他的元素ai有且仅有一个前驱ai-1和后继ai+1

顺序表的实现

1、基本内容

顺序表就是线性表的顺序存储,常用sqlist来表示。顺序存储在编程中用数组的方式来实现。

顺序表结构体如下:

#define N 100         //最大存储的数据元素个数
typedef int data_t;   //数据元素的类型,可以为int,也可以是一个结构体
//顺序表
typedef struct{
    data_t data[N];   //申请的存储空间
    int last;         //最后一个数据的索引值,data[N]是最大空间,last代表实际用了多少空间
}sqlist,*sqlink;

顺序表代码的文件构成:

  • sqlist.h:数据结构的定义、运算函数接口
  • sqlist.c:运算函数接口的实现
  • test.c:使用数据结构实现的应用功能代码

2、功能实现

2.0 编程注意点

2.0.1 防御式编程

函数编写的常用条件判断:

1、当传入参数为指针时,需要进行空指针判断

//判断顺序表传入的指针是否为空
if(pLink == NULL){
		printf("pLink is NULL\n");
		return;
}

2、当使用malloc申请内存时,需要对申请的空间进行空指针判断

//判断申请的空间是否为空
pLink = (sqlink)malloc(sizeof(sqlist));
if(pLink == NULL){
    printf("malloc failed\n");
	return NULL;
}

3、对于空间固定的结构,需要访问是判断访问下标是否在合理范围内

//在插入、删除函数中,需要判断传入的位置是否在合理范围
if( location < 0 || location > pLink->last){
	printf("location err\n");
	return -1;
}

4、对于空间固定的结构,需要在新增成员前判断是否还存在剩余空间

2.0.2 函数分类

顺序表相关函数:

1、整个表的创建和删除

  • sqlink list_create(void)
  • void list_delete(sqlink pLink)

2、 表中元素的增删改查

  • 插入:int list_insert(sqlink pLink,data_t value,int location)
  • 删除:int list_delete_data(sqlink pLink,int location)
  • 查找:int list_isexsit(sqlink pLink,data_t value)
  • 获取:data_t* list_get(sqlink pLink,int location)
  • 清空:int list_clean(sqlink pLink)

3、获取表的属性

  • 元素个数:int list_length(sqlink pLink)
  • 是否为空:int list_isempty(sqlink pLink)

4、其他

  • 删除重复元素:int list_purge(sqlink pLink)
  • 合并两个顺序表:int list_merge(sqlink pLink1,sqlink pLink2)

2.1 创建/删除表

2.1.1 创建

创建表就是申请一个空间,并给这个空间进行初始化。具体代码实现如下:

/*
 * list_create:创建一个空表
 * @ret 顺序表指针--success NULL--failed
 * */
sqlink list_create(void){
	
	sqlink pLink = NULL;
	//1.申请空间
	pLink = (sqlink)malloc(sizeof(sqlist));
	if(pLink == NULL){
		printf("malloc failed\n");
		return NULL;
	}
	//2.初始化
	memset(pLink->data,0,N*sizeof(data_t));
	pLink->last = -1;
	printf("Debug:at list_create,last = %d\n",pLink->last);
	return pLink;
}
2.1.2 删除表

删除表就是释放创建时申请的空间。具体代码实现如下:

/*
 * list_delete:删除顺序表,释放空间
 * param pLink:顺序表指针
 * */
void list_delete(sqlink pLink){
	//1.表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return;
	}
	//2.释放空间
	free(pLink);
	pLink = NULL;//这里不是想让main中的指针指向NULL
				 //当释放空间后,需要把指针指向NULL
}

2.2 元素的增删改查

2.2.1 插入

插入函数传参分析:

代码功能:

向顺序表中指定位置插入指定元素。因此需要3个变量:顺序表指针、位置、变量

函数声明如下:

int list_insert(sqlink pLink,data_t value,int location)

函数的传参需要进行有效性的判断:传入的指针是否为空、传入的位置是否符合有效范围。除此之外,因为顺序表是一个空间固定的表,所以还需要判断当前表中是否还要空余位置

插入代码分析:

假设我们想要在1位置去插入,那么首先需要去腾出一个位置,即:把1移到2,把2移到3...直到最后一个last移到last+1处。之后在1位置处去插入我们所需要的值即可。具体示意图如下:

特殊的,如果想插入的位置在last后,这样就不要去移动了,只需要去插到last+1处即可。

注意:在插入之后,last需要加一,因为多出来了一个元素。

具体代码实现如下:

/*
 * list_insert:向顺序表指定位置插入指定的数据
 * param pLink:顺序表指针
 * param value:要插入的数据
 * param location:要插入的位置
 * @ret  -1--err 0--success
 * */
int list_insert(sqlink pLink,data_t value,int location){
	
	int i;
	//1.参数有效性判断
	//1.1 表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//1.2 位置判断
	if( location < 0 || location >= N){
		printf("location err\n");
		return -1;
	}
	//2.判断表是否为满
	if(pLink->last >= N-1){
		printf("list is full\n");
		return -1;
	}

	//3.插入数据
	if(location > pLink->last){//3.1插入位置在last后
		pLink->data[pLink->last+1] = value;
	}else{//3.2插入位置在last前
		//移出位置
		i = pLink->last;
		while(i != location-1){
			pLink->data[i+1] = pLink->data[i];
			i--;
		}
		//插入数据
		pLink->data[location] = value;
	}
	pLink->last++;
	return 0;
}
2.2.2 删除

删除函数传参分析:

代码功能:

从顺序表中指定位置删除指定元素。因此需要2个变量:顺序表指针、位置

函数声明如下:

int list_delete_data(sqlink pLink,int location)

函数的传参需要进行有效性的判断:传入的指针是否为空、传入的位置是否符合有效范围

删除代码分析:

假设我们想要删除1位置的数据,那么只需要把后面的数据覆盖1位置即可,即:把2移到1,把3移到2...直到最后一个last移到last-1处。之后去清除原last位置处的数据。具体示意图如下:

注意:在插入之后,last需要减一,因为少了一个元素。

具体代码实现如下:

/*
 * list_delete_data
 * param pLink:顺序表指针
 * param location:要删除的数据位置
 * @ret  -1--err  0--success
 * */
int list_delete_data(sqlink pLink,int location){
	int i;
	//1.参数有效性判断
	//1.1 表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//1.2 位置判断
	if( location < 0 || location > pLink->last){
		printf("location err\n");
		return -1;
	}

	//2.删除元素
	i=location;
	while(i!=pLink->last){
		pLink->data[i] = pLink->data[i+1];
		i++;
	}
	memset(&(pLink->data[pLink->last]),0,sizeof(data_t));
	pLink->last--;
	return 0;
}
2.2.3 查询

查询函数传参分析:

代码功能:

查询某个元素是否在顺序表中,并返回第一个出现的下标。因此需要2个变量:顺序表指针、位置

函数声明如下:

int list_isexsit(sqlink pLink,data_t value)

函数的传参需要进行有效性的判断:传入的指针是否为空。

具体代码实现如下:

/*
 * list_isexsit:判断顺序表中是否有某个元素
 * param pLink:顺序表指针
 * @ret  -2--err 
 * @ret  -1--not exsit  other--第一个找到的元素位置
 * */
int list_isexsit(sqlink pLink,data_t value){
	int i=0;
	//1.表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -2;
	}
	//2.遍历查找元素
	for(i=0;i<=pLink->last;i++){
		if(pLink->data[i] == value){
			//printf("find it\n");
			return i;
		}
	}
	//printf("not find\n");
	return -1;
}
2.2.4 获取

获取函数传参分析:

代码功能:

获取顺序表中指定位置的元素地址。因此需要2个变量:顺序表指针、位置

函数声明如下:

data_t* list_get(sqlink pLink,int location)

函数的传参需要进行有效性的判断:传入的指针是否为空、传入的位置是否符合有效范围

具体代码实现如下:

/*
 * list_get:获取顺序表中指定的数据的指针
 * param pLink:顺序表指针
 * param location:要获取数据的位置
 * @ret  NULL--failed  找到的数据的指针--success
 * */
data_t* list_get(sqlink pLink,int location){
	
	data_t* point = NULL;
	//1.参数有效性判断
	//1.1 表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return NULL;
	}
	//1.2 位置判断
	if( location < 0 || location >= N){
		printf("location err\n");
		return NULL;
	}
	//2.获取顺序表中指定的数据的指针
	if(location > pLink->last){//获取位置没有数据
		//printf("no data here \n");
	}
	point = &pLink->data[location];
	return point;
}
2.2.5 清空

具体代码实现如下:

/*
 * list_clean:清空顺序表
 * param pLink:顺序表指针
 * @ret  -1--err  0--success
 * */
int list_clean(sqlink pLink){
	//1.表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//2.清除表
	memset(pLink->data,0,N*sizeof(data_t));
	pLink->last = -1;
	return 0;	
}

2.3 获取表的属性

2.3.1 元素个数
/*
 * list_length:获取顺序表的长度
 * param pLink:顺序表指针
 * @ret  -1--err other--长度 
 * */
int list_length(sqlink pLink){
	//1.表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//2.返回长度
	return (pLink->last+1);
}
2.3.2 是否为空
/*
 * list_isempty:判断表是否为空
 * param pLink:顺序表指针
 * @ret  -1--err  0--not empty 1--empty 
 * */
int list_isempty(sqlink pLink){
	//1.表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//2.判断是否非空
	if(pLink->last == -1){
		printf("sqlist empty\n");
		return 1;
	}else{
		printf("sqlist not empty\n");
		return 0;
	}
}

2.4 其他

2.4.1 删除重复元素
/*
 * list_purge:删除重复的元素
 * param pLink:顺序表指针
 * @ret -1--err 0--success
 * */
int list_purge(sqlink pLink){
	int i;
	int FindId;
	//1.表指针判断
	if(pLink == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//2.元素少于2个时候,不需要处理
	if(pLink->last <= 0){
		return 0;
	}
	//3.开始删除
	for(i=1;i<=pLink->last;i++){
		//找到第一个出现的位置
		FindId = list_isexsit(pLink,pLink->data[i]);
		//找到了重复的值,第一次位置与当前位置不一样
		if(FindId != i){
			list_delete_data(pLink,i);
			i--;
		}		
	}
	return 0;
}
2.4.2 合并两个顺序表
/*
 * list_merge:将顺序表1和顺序表2进行合并
 *            表2不变,表1中添加表2中的非同类项
 * param pLink:顺序表指针
 * @ret -1--err 0--success
 * */
int list_merge(sqlink pLink1,sqlink pLink2){
	int i=0;
	//1.表指针判断
	if(pLink1 == NULL || pLink2 == NULL){
		printf("pLink is NULL\n");
		return -1;
	}
	//2.遍历2,查找是否在1中,不在则追加到尾部
	for(i=0;i<=pLink2->last;i++){
		//1满了,提前退出插入
		if(pLink1->last == N-1){
			printf("sqlist1 full\n");
			break;
		}
		//如果2中的数据1中不存在,则插入到尾部
		if(list_isexsit(pLink1,pLink2->data[i]) == -1){
			list_insert(pLink1,pLink2->data[i],pLink1->last+1);
		}		
	}
	return 0;
}

链表的实现

1、基本内容

链表就是线性表的链式存储,常用listnode来表示。链式存储在编程中用指针链接的方式来实现。

链表结构体如下:

typedef int data_t;   //数据元素的类型,可以为int,也可以是一个结构体
//链表
typedef struct node{
    data_t data;        //数据
    struct node* pNext; //链表指针,指向下一个链表结点
}listnode,*linklist;

链表代码的文件构成:

  • listnode.h:数据结构的定义、运算函数接口
  • listnode.c:运算函数接口的实现
  • test.c:使用数据结构实现的应用功能代码

2、功能实现

2.0 编程注意点

2.0.1 链表移动注意点

链表移动、插入时插入考虑三种情况:

  • 插入点前没有结点(最前面)
  • 插入点前后都有结点(中间)
  • 插入点后面没有结点(最后面)

插入之前需要保存现场:保存前一个结点和后一个结点的位置,这样才能保证链表不丢失。

2.0.2 函数分类

链表相关函数:

1、整个表的创建和删除

  • 创建:linklist list_create(void)
  • 删除:int list_delete(linklist* pLink)

2、 表中元素的增删改查

  • 尾插:int list_insert_tail(linklist pLink,linklist newLink)
  • 指定位置插入:int list_insert_location(linklist* pLink,linklist newLink,int location)
  • 删除:int list_delete_location(linklist* pLink,int location)
  • 获取:linklist list_get(linklist pLink,int location)

3、其他

  • 链表反转:int list_reversal(linklist* pLink)
  • 获取相邻两个结点数据之和最大值的指针:linklist get_MaxSumNode(linklist pLink)
  • 合并两个有序链表:linklist list_merge(linklist pLink1,linklist pLink2)
  • 链表排序:linklist list_sort(linklist pLink)

2.1 创建/删除链表

2.1.0 创建

创建链表就是申请一个空间,并给这个空间进行初始化。对于新链表,pNext应该指向NULL。

具体代码实现如下:

/*
 * list_create:创建一个链表结点
 * @ret  NULL--创建失败  other--创建的链表结点首地址
 * */
linklist list_create(void){
	
	linklist pLink = NULL;
	//1.申请空间
	pLink = (linklist)malloc(sizeof(listnode));
	if(pLink == NULL){
		printf("malloc failed\n");
		return NULL;
	}
	//2.初始化
	memset(&pLink->data,0,sizeof(data_t));
	pLink->pNext = NULL;
	return pLink;
}
2.1.1 删除

删除整个链表就是用遍历的方式释放每一个链表结点。最后要把释放后的链表头变为NULL。

具体代码实现如下:

/*
 * list_delete:释放整个链表
 * param pLink:链表头
 * @ret  -1--err  0--success
 * */
int list_delete(linklist* pLink){
	linklist point = *pLink;
	linklist pTmp = NULL;
	//1.判断结点非空
	if(*pLink == NULL){
		printf("listnode is NULL\n");
		return -1;
	}
	//2.释放整个链表
	while(point!=NULL){
		pTmp = point;
		point = point->pNext;
		free(pTmp);
	}
	*pLink = NULL;//释放之后需要把头结点设为空
	return 0;
}

2.2 元素的增删改查

2.2.1 尾插

尾插法比较简单,首先需要遍历链表找到尾部,之后让尾部指向新的链表即可。如果想提高效率,可以定义一个尾结点,这样就不需要每次遍历寻找尾部了。

具体代码实现如下:

/*
 * list_insert_tail:尾部插入链表结点
 * param pLink:插入哪一个链表
 * param newLink:要插入的链表结点
 * @ret  -1--err  0--success
 * */
int list_insert_tail(linklist pLink,linklist newLink){
	
	linklist point = pLink;
	//1.判断结点非空
	if(pLink == NULL || newLink == NULL){
		printf("listnode is NULL\n");
		return -1;
	}
	//2.开始插入
	//2.1 找到尾部
	while(point->pNext != NULL){
		point = point->pNext;
	}
	//2.2插入新结点
	point->pNext = newLink;
	return 0;
}
2.2.2 指定位置插入

指定位置插入就是按照所给的序号进行插入,这里需要考虑三种情况:

  • 插入点前没有结点(最前面)
  • 插入点前后都有结点(中间)
  • 插入点后面没有结点(最后面)

 具体框图如下:

情况1的插入方式特殊,需要将头进行改变。情况2和情况3都是将链表插入到p1,p2之间即可。

具体的插入条件与步骤如下:

从上述分析可以看到,进行复杂插入的关键是保存插入点前后的结点位置。在下面的代码中并没有使用单独的p1,p2来保存插入点前后的结点位置,但逻辑依旧与上面的分析一致。

具体的代码实现如下:

/*
 * list_insert_location:在指定的序号元素后插入结点
 * 						-1代表在头前面插入
 * param pLink:要在哪里插入结点
 * param newLink:插入的新结点
 * param location:插入的位置
 * @ret  -1--err  0--success
 * */
int list_insert_location(linklist* pLink,linklist newLink,int location){
	linklist point = NULL;
	linklist pNextTmp = NULL;
	//1.参数有效性判断
	//1.1判断结点非空
	if(*pLink == NULL || newLink == NULL){
		printf("listnode is NULL\n");
		return -1;
	}
	//1.2 判断位置
	if(location < -1){
		printf("location err\n");
		return -1;
	}
	//2.特殊位置插入,头前面插入
	if(location == -1){
		newLink->pNext = *pLink;
		*pLink = newLink;
		return 0;
	}
	//3.获取位置
	point = list_get(*pLink,location);
	//4.在指定位置后,插入新结点
	if(point != NULL){
		pNextTmp = point->pNext;//p2保存后结点位置
		point->pNext = newLink; //p1就是point
		newLink->pNext = pNextTmp;//new指向p2
	}else{
		list_insert_tail(*pLink,newLink);
	}
	return 0;
}
2.2.3 删除

删除指定位置的链表结点就是释放空间并链接前后的结点。(删除与"2.2.2插入"的思想完全一致)

  1. 首先需要遍历找到这个结点,遍历同时使用p1,p2保存删除点前后的结点位置
  2. 之后将前后结点链接,并释放要删除的结点的空间。
/*
 * list_delete_location:删除指定位置的链表结点
 * param pLink:要在哪里寻找结点
 * param location:结点的序号
 * @ret -1--err 0--success
 * */
int list_delete_location(linklist* pLink,int location){
	linklist point = *pLink;
	linklist pTmp = NULL;
	int i = 0;
	//1.参数有效性判断
	//1.1判断结点非空
	if(*pLink == NULL){
		printf("listnode is NULL\n");
		return -1;
	}
	//1.2位置判断
	if(location < 0){
		printf("location err\n");
		return -1;
	}
	//2.获取结点
	while(point!=NULL){
		if(i == location){
			break;
		}
		pTmp = point;
		point = point->pNext;
		i++;
	}
	if(point == NULL){//没找到
		printf("not find\n");
		return -1;
	}
	//3.开始删除
	if(point->pNext == NULL){//要删除的是尾结点
		pTmp->pNext = NULL;
		free(point);
		point = NULL;
	}else if(pTmp == NULL){//要删除的是头结点
		*pLink = point->pNext;//改变头的位置
		free(point);
		point = NULL;
	}else{//要删除的是中间结点
		pTmp->pNext = point->pNext;
		free(point);
		point = NULL;
	}
	return 0;
}
2.2.4 获取

按序号寻找链表结点就是遍历链表,第一个结点是序号0。

每次遍历比对序号,找到返回指针即可。

具体函数实现如下:

/*
 * list_get:按序号寻找链表结点
 * param pLink:要在哪里寻找结点
 * param location:结点的序号
 * @ret  NULL--not find或者err  other--第一个结点首地址
 * */
linklist list_get(linklist pLink,int location){
	
	int i=0;
	linklist point = pLink;
	//1.参数有效性判断
	//1.1判断结点非空
	if(pLink == NULL){
		printf("listnode is NULL\n");
		return NULL;
	}
	//1.2位置判断
	if(location < 0){
		printf("location err\n");
		return NULL;
	}
	//2.遍历寻找
	while(point!=NULL){
		if(i==location){//找到了
			break;
		}
		i++;
		point = point->pNext;
	}
	if(point == NULL){
		printf("not find\n");
	}
	return point;
}

2.3 其他

2.3.1 链表反转

链表反转就是将头变成尾,将尾变成头。

  1. 首先需要把头1与之后的链子断开
  2. 之后将断开的链子的头2插入到头1的前面即可。

具体框图如下:

具体代码实现如下:

/*
 * list_reversal:链表反转
 * param pLink:链表头指针
 * @ret  -1--err  0--success
 * */
int list_reversal(linklist* pLink){
	linklist pHead = *pLink;
	linklist pInTmp = NULL;
	linklist pTmp = NULL;
	//1.判断结点非空
	if(*pLink == NULL){
		printf("listnode is NULL\n");
		return -1;
	}
	//2.判断是否为单个结点
	if((*pLink)->pNext == NULL){
		printf("just have 1 node\n");
		return 0;
	}
	//3.断开头与之后数据,pInTmp暂存第2个结点,这也将要插入的结点
	pInTmp = pHead->pNext;
	pHead->pNext = NULL;
	//4.将结点进行插入
	while(1){
		pTmp = pInTmp->pNext;//暂存第3个结点
		pInTmp->pNext = pHead;//第2个结点指向第1个
		pHead = pInTmp;//头部变成了第2个结点
		pInTmp = pTmp;//插入结点变成第3个
		if(pTmp == NULL){
			*pLink = pHead;
			break;
		}
	}
	return 0;
}
2.3.2 获取相邻两个结点数据之和最大值的指针

获取相邻两个结点数据之和最大值的指针就是移动两个指针,不断比较求和值,如果后面的大于前面的,就保存后面的结点位置即可。

具体代码实现如下:

/*
 * get_MaxSumNode:获取相邻两个结点数据之和最大的第一个结点的指针
 * param pLink:要在哪里寻找结点
 * @ret  NULL--err  other--第一个结点首地址
 * */
linklist get_MaxSumNode(linklist pLink){
	int sum = 0;
	linklist point = NULL;
	linklist pRet = pLink;
	//1.判断结点非空
	if(pLink == NULL){
		printf("listnode is NULL\n");
		return NULL;
	}
	//2.判断是否为单个结点
	if(pLink->pNext == NULL){
		printf("just have 1 node\n");
		return NULL;
	}
	//3.开始计算
	point = pLink;
	sum = point->data + point->pNext->data;//初始化sum值
	while(point->pNext != NULL){
		if( sum < (point->data + point->pNext->data) ){
			sum = point->data + point->pNext->data;
			pRet = point;
		}
		point = point->pNext;
	}
	return pRet;
}
2.3.3 合并两个有序链表

合并两个有序链表是将两个递增有序的链表合并为一个递增有序的链表。

  1. 首先需要找到头部,即两个链表的头中较小的那个结点为头部。
  2. 之后随便遍历一个链表,遍历对两个链表的数值进行比较,谁小谁就插入到头后面,进行插入的链表需要将指针进行移动,未插入的链表指针不用移动。
  3. 最后判断是哪一个链表没有遍历完,将整个链表全部尾插即可。

具体的代码实现如下:

/*
 * list_merge:将两个递增有序的链表合并为一个递增有序的链表
 * param pLink1:第一个链表
 * param pLink2:第二个链表
 * @ret  NULL--err  other--合成后的链表的头
 * */
linklist list_merge(linklist pLink1,linklist pLink2){
	linklist pHead = NULL;
	linklist pTail = NULL;
	//1.判断结点非空
	if(pLink1 == NULL || pLink2 == NULL){
		printf("listnode is NULL\n");
		return NULL;
	}
	//2.确定头部,谁小谁就是头部
	if(pLink1->data < pLink2->data){
		pHead = pLink1;
		pLink1 = pLink1->pNext;//向后移动一个,指向下一个将要插入的结点
	}else{
		pHead = pLink2;
		pLink2 = pLink2->pNext;//向后移动一个,指向下一个将要插入的结点
	}
	pTail = pHead;
	//3.比较大小,谁小谁进行尾插
	while(pLink1!=NULL && pLink2!=NULL){
		if(pLink1->data < pLink2->data){
			pTail->pNext = pLink1;
			pTail = pLink1;
			pLink1 = pLink1->pNext;
		}else{
			pTail->pNext = pLink2;
			pTail = pLink2;
			pLink2 = pLink2->pNext;
		}
	}
	//4.将剩余元素进行尾插
	if(pLink1 == NULL && pLink2 != NULL){
		pTail->pNext = pLink2;
	}
	if(pLink2 == NULL && pLink1 != NULL){
		pTail->pNext = pLink1;
	}
	return pHead;
}
2.3.4 链表排序

这个链表排序是练习链表使用的,使用的方法是将冒泡排序法。

关键点是交换时前后结点的位置保存、还有交换后的指针移动。

具体代码实现如下:

/*
 * list_sort:链表排序
 * param pLink:要进行排序的链表头
 * @ret  NULL--err  other--排序后的链表头
 * */
linklist list_sort(linklist pLink){
	linklist pHead = pLink;
	linklist p1 = NULL;
	linklist p2 = NULL;
	linklist pLast1 = NULL;
	linklist pTmp2 = NULL;
	int link_len = 0;
	int i,j;
	//1.判断结点非空
	if(pLink == NULL){
		printf("listnode is NULL\n");
		return NULL;
	}
	//2.获取链表长度
	p1 = pHead;
	while(p1!=NULL){
		link_len++;
		p1 = p1->pNext;
	}
	//printf("Debug:link_len = %d\n",link_len);
	if(link_len < 3){
		return pHead;
	}
	//3.开始排序,冒泡排序
	for(i=0;i<link_len-1;i++){
		pLast1 = NULL;
		p1 = pHead;
		p2 = pHead->pNext;
		//printf("****************head data = %d*************\n",pHead->data);
		for(j=0;j<link_len-1-i;j++){
			if(p1->data > p2->data){
				//p1在头部
				if(pLast1 == NULL){
					//printf("=========p1在头部 start==============\n");
					//printf("%d %d %d %d\n",pHead->data,pHead->pNext->data,pHead->pNext->pNext->data,pHead->pNext->pNext->pNext->data);	
					//更改头部
					pHead = p2;
					//printf("data = %d\n",pHead->data);
					//开始链接
					pTmp2 = p2->pNext;
					p2->pNext = p1;
					p1->pNext = pTmp2;
					//printf("==========link=============\n");
					//printf("%d %d %d %d\n",pHead->data,pHead->pNext->data,pHead->pNext->pNext->data,pHead->pNext->pNext->pNext->data);	
					//指针移动
					pLast1 = p2;
					p1 = p1;
					p2 = p1->pNext;
					//printf("===========p move============\n");
					//printf("pLast1:%d p1:%d p2:%d\n",pLast1->data,p1->data,p2->data);	
					//sleep(1);
				}
				//p1在中间
				else{
					//printf("\n=========p1在中间start==============\n");
					//printf("%d %d %d %d\n",pHead->data,pHead->pNext->data,pHead->pNext->pNext->data,pHead->pNext->pNext->pNext->data);	
					//开始链接
					pLast1->pNext = p2;
					pTmp2 = p2->pNext;
					p2->pNext = p1;
					p1->pNext = pTmp2;
					//printf("==========link=============\n");
					//printf("%d %d %d %d\n",pHead->data,pHead->pNext->data,pHead->pNext->pNext->data,pHead->pNext->pNext->pNext->data);	
					//指针移动
					pLast1 = p2;
					p1 = p1;
					p2 = p1->pNext;
					//printf("===========p move============\n");
					//printf("pLast1:%d p1:%d \n",pLast1->data,p1->data);
					//sleep(1);
				}
			}else{
				//printf("\n=========不改变位置start==============\n");
				//printf("%p %p %p %p\n",pHead,pHead->pNext,pHead->pNext->pNext,pHead->pNext->pNext->pNext);	
				pLast1 = p1;
				p1 = p2;
				p2 = p2->pNext;
				//printf("===========p move============\n");
				//printf("%p %p %p %p\n",pLast1,p1,p2,p2->pNext);
				//sleep(1);
			}
		}
	}	

	return pHead;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值