考研数据结构之线性表(1.2)——单链表的操作(C表示)

目录

单链表的操作

单链表的插入操作

题目

思路

代码

测试结果

算法总结

 

单链表的删除、查找操作

题目

思路

代码

测试结果

算法总结

单链表操作总结

思路

代码


单链表的操作

单链表的插入操作

题目

A和B是两个单链表(带表头结点),其中元素递增有序。设计一个算法,将A和B归并成一个按元素值非递减有序的链表C,C由A和B的结点组成。

思路

(1)已知A、B中的元素都是递增有序的,要使归并后C中的元素依然是有序的,从A,B中挑选出最小的元素插入C的尾部,这样当A、B中的所有元素都插入C中时,C一定是递增有序的。

(2)哪个是最小的元素呢?由于A、B是递增的,因此A中的最小元素是其开始结点中的元素,B也是一样,只需从A、B的开始结点中选出一个较小的来插入到C的尾部即可。

(3)需要注意,A与B中的元素有可能一个已经全部被插入到C中,另一个还没有插完,这时候只需要将没有插完哪个链接到C的尾部即可,两个都可以这样来处理。

代码

#include <stdio.h>
#include <stdlib.h>
// 声明结构体
struct LNode {
	int data;
	struct LNode *next;
};

/* 创建一个单链表,由用户输入整型测试数据 */
/* 返回一个单链表 */
LNode * createList() {
	LNode *head;
	LNode *p1,*p2;
	p1=p2=(LNode *)malloc(sizeof(LNode));
	scanf("%ld",&p1->data);
	head=NULL;
	int i=0;
	while(p1->data!=0) { // 当用户在控制台输入0时结束循环
		i+=1;
		if(i==1) {
			head=p1;
		} else {
			p2->next=p1;
		}
		p2=p1;
		p1=(LNode *)malloc(sizeof(LNode));
		scanf("%ld",&p1->data);
	}
	p2->next=NULL;
	return head;
}

/* 打印单链表 */
/* *list指的是要被打印输出的单链表 */
void printList(LNode *list) {
	printf("\n");
	while(list!=NULL) { // 循环单链表
		printf("%ld\t",list->data); // 打印单链表中的data数据
		list=list->next; // 遍历至下一个结点
	}
	printf("\n"); // 换行
}


/* 归并递增:将两个递增单链表归并成一个递增单链表(不带头节点),返回合并后的递增单链表 */
/* *A指的是递增单链表A;*B指的是递增单链表B;*&C是带头结点的单链表 */
void * merge(LNode *A,LNode *B,LNode *&C) {
	LNode *p;
	LNode *q;
	p=A;// 注意:这里A传入的是不带头节点的单链表,如果A是带头结点的单链表,那么这里应该是p=A->next
	q=B;// 注意:这里B传入的是不带头节点的单链表,如果B是带头结点的单链表,那么这里应该是p=B->next
	/* 创建一个带头节点的单链表start */
	LNode *temp;
	C=(LNode *)malloc(sizeof(LNode));// 创建一个带头节点的链表
	C->next=NULL;// 设置头节点的下一个结点为NULL
	temp=C;// 临时保存头节点,也是尾结点,参与下面的结点连接操作
	// (temp既相当于链表的头节点,又相当于链表的尾结点,完成的链表就是head->next)
	/* 创建一个带头节点的单链表end */
	while(p!=NULL&&q!=NULL) { // 在同时满足p和q链表不为NULL的情况下循环
		// 判断p链表中结点的数据是否小于等于q链表中的数据
		if(p->data<=q->data) { // 如果p->data比q->data小于等于,那么
			// 体现了尾插法的思想
			temp->next=p; // 将临时头节点的下一个结点指向p
			p=p->next;// 并且p指向下一个结点
			temp=temp->next; // 并且temp也指向终端结点
		} else { // 反之,如果大,那么将q链表中的该结点添加到temp尾
			temp->next=q;
			q=q->next;
			temp=temp->next;
		}
	}
	if(p!=NULL) { // 处理q链表已经全部插入到新链表,而p链表还剩下些结点,那么将这些结点链接到temp尾
		temp->next=p;
	}
	if(q!=NULL) { // 同上
		temp->next=q;
	}
}

/* 归并递减:将两个递增单链表归并成一个递减的单链表,返回一个递减的单链表 */
/* *A指的是递增单链表A;*B指的是递增单链表B */
LNode * mergeDe(LNode *A,LNode *B) {
	LNode *C,*temp,*p;
	LNode *a=A,*b=B;
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	while(a!=NULL&&b!=NULL) {
		if(a->data<=b->data) {
			// 为什么需要使用临时结点p?因为p要作为插入的新结点使用又要指向终端结点,不能混合交叉使用,所以需要一个临时结点来保存
			p=a; // 临时保存a结点,即新要插入到链表头部的新结点
			a=a->next; // 到a的下一个结点
			p->next=temp->next;  // 头插法的经典使用
			temp->next=p;
		} else {
			p=b;
			b=b->next;
			p->next=temp->next;
			temp->next=p;
		}
	}
	while(a!=NULL) {
		p=a;
		a=a->next;
		p->next=temp->next;
		temp->next=p;
	}
	while(b!=NULL) {
		p=b;
		b=b->next;
		p->next=temp->next;
		temp->next=p;
	}
	return C->next;
}

/* 使用尾插法建立单链表,即新添加的元素添加到链表的尾部 */
/* *&C指的是建立的单链表;a[]指的是批量添加的元素数组;n表示数组长度 */
void createListL(LNode *&C,int a[],int n) {
	LNode *temp,*node;
	// 创建一个带头节点的单链表
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	// 循环遍历数组,批量为链表插入数据
	for(int i=0; i<n; i++) { // 循环申请n个结点来接收a中的元素
		node=(LNode *)malloc(sizeof(LNode)); // 创建新结点
		node->data=a[i]; // 为新结点赋予数据
		temp->next=node; // 将原尾结点的下一个结点指向新结点
		temp=temp->next; // 指向终端结点,以便接收下一个结点
	}
	temp->next=NULL; // 所有元素已经填装完成,将终端结点置为NULL
}

/* 使用头插法建立单链表,即新添加的元素添加到链表的头部 */
/* *&C指的是建立的单链表;a[]指的是批量添加的元素数组;n表示数组长度 */
void createListF(LNode *&C,int a[],int n) {
	LNode *temp,*node;
	// 创建带头节点的单链表
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	for(int i=0; i<n; i++) {
		node=(LNode *)malloc(sizeof(LNode));// 新建结点
		node->data=a[i];// 为新结点赋值
		// 注意:下面两行不能交换位置,否则会进入死循环
		node->next=temp->next; // node所指新结点的指针域next指向C的开始结点
		temp->next=node; // 头结点的指针域next指向node,使得node成为新的开始结点
	}
}


int main() {
	/* [0.]创建初始测试单链表 */
	LNode *list1,*t1,*t2,*list11,*list22;
	LNode *list2,*mergeList,*mergeDeList;

	/* [1.]归并递增 */
	list1=createList();// 创建测试链表
	printList(list1);// 打印单链表
	list2=createList();
	printList(list2);
	merge(list1,list2,mergeList);
	printList(mergeList->next);

	/* [2.]归并递减 */
	list11=createList();// 创建测试链表
	printList(list11);// 打印单链表
	list22=createList();
	printList(list22);
	mergeDeList=mergeDe(list11,list22);
	printList(mergeDeList);

	/* [3.]尾插法 */
	int a[]= {1,3,4,6,7,9,2,5,8};
	createListL(t1,a,9);
	printList(t1->next);

	/* [4.]头插法 */
	int b[]= {1,3,4,6,7,9,2,5,8};
	createListF(t2,b,9);
	printList(t2->next);

	return 0;
}

测试结果

算法总结

尾插法建立单链表的核心代码:

/* 使用尾插法建立单链表,即新添加的元素添加到链表的尾部 */
/* *&C指的是建立的单链表;a[]指的是批量添加的元素数组;n表示数组长度 */
void createListL(LNode *&C,int a[],int n) {
	LNode *temp,*node;
	// 创建一个带头节点的单链表
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	// 循环遍历数组,批量为链表插入数据
	for(int i=0; i<n; i++) { // 循环申请n个结点来接收a中的元素
		node=(LNode *)malloc(sizeof(LNode)); // 创建新结点
		node->data=a[i]; // 为新结点赋予数据
		temp->next=node; // 将原尾结点的下一个结点指向新结点
		temp=temp->next; // 指向终端结点,以便接收下一个结点
	}
	temp->next=NULL; // 所有元素已经填装完成,将终端结点置为NULL
}

头插法建立单链表的核心代码:

/* 使用头插法建立单链表,即新添加的元素添加到链表的头部 */
/* *&C指的是建立的单链表;a[]指的是批量添加的元素数组;n表示数组长度 */
void createListF(LNode *&C,int a[],int n) {
	LNode *temp,*node;
	// 创建带头节点的单链表
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	for(int i=0; i<n; i++) {
		node=(LNode *)malloc(sizeof(LNode));// 新建结点
		node->data=a[i];// 为新结点赋值
		// 注意:下面两行不能交换位置,否则会进入死循环
		node->next=temp->next; // node所指新结点的指针域next指向C的开始结点
		temp->next=node; // 头结点的指针域next指向node,使得node成为新的开始结点
	}
}

 

单链表的删除、查找操作

题目

查找链表C(带头结点)中是否存在一个值为x的结点,若存在,则删除该结点并返回1,否则返回0。

思路

(1)定义一个指针变量p,让它一直走到表尾,每遇到一个新结点,就检测其值是否为x,是则证明找到了,否则继续检测下一个结点。

(2)当找到值为x的结点后就删除该结点。

代码

#include <stdio.h>
#include <stdlib.h>
// 声明结构体
struct LNode {
	int data;
	struct LNode *next;
};

/* 创建一个单链表,由用户输入整型测试数据 */
/* 返回一个单链表 */
LNode * createList() {
	LNode *head;
	LNode *p1,*p2;
	p1=p2=(LNode *)malloc(sizeof(LNode));
	scanf("%ld",&p1->data);
	head=NULL;
	int i=0;
	while(p1->data!=0) { // 当用户在控制台输入0时结束循环
		i+=1;
		if(i==1) {
			head=p1;
		} else {
			p2->next=p1;
		}
		p2=p1;
		p1=(LNode *)malloc(sizeof(LNode));
		scanf("%ld",&p1->data);
	}
	p2->next=NULL;
	return head;
}

/* 打印单链表 */
/* *list指的是要被打印输出的单链表 */
void printList(LNode *list) {
	printf("\n");
	while(list!=NULL) { // 循环单链表
		printf("%ld\t",list->data); // 打印单链表中的data数据
		list=list->next; // 遍历至下一个结点
	}
	printf("\n"); // 换行
}

/* 求单链表的表长的算法 */
/* *list指的是单链表 */
int length(LNode *list) {
	LNode *temp=list;// 临时指针变量
	int i=0; // 计数器,用来统计表长,初始长度为0
	while(temp!=NULL) { // 循环条件,当单链表中结点不为NULL
		temp=temp->next; // 指向下一个结点
		i++; // 计数器加1
	}
	return i;
}

/* 按序号查找的算法:findKth */
/* num表示结点的序号(不是下标);*list指的是要进行查找操作的单链表 */
LNode * findKth(int num,LNode *list) {
	LNode *temp=list;
	int i=1;// 计数器,统计当前是第几个结点
	while(temp!=NULL&&i<num) { // 循环遍历结点,判断链表是否为空并且计数器小于查找查找序号(退出while循环后,i==K)
		i+=1; // 计数器加1
		temp=temp->next; // 指向下一个结点
	}
	if(i==num) { // 判断是否查找到序号
		return temp;// 返回查找到的结点
	} else {
		return NULL;
	}
}

/* 按值查找算法:find */
/* data指要查找的data;*list指的是要在哪个链表中查询 */
LNode * find(int data,LNode *list) {
	LNode *temp=list;
	while(temp!=NULL&&temp->data!=data) { // 遍历循环,当temp->data=x时跳出循环,temp即为所求
		temp=temp->next;
	}
	return temp;
}

/* 在单链表中插入新结点 */
/* data指的是要插入的数据;p指的是要插入的结点序号位置(不是下标);*list指的是要插入的单链表 */
LNode * insert(int data,int p,LNode *list) {
	LNode *newNode,*priorNode;
	/* 分情况讨论:(1)插入位置在首位;(2)插入位置不在首位 */
	/* (1)插入位置在首位的情况 */
	if(p==1) { // 如果要插入的位置是第一个结点
		newNode=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点
		newNode->data=data; // 指定新结点的数据
		newNode->next=list; // 指定新结点的下一个结点的地址(如果是插入在首位,那么下一个结点的地址就是链表list的第一个结点的地址)
		return newNode; // 返回结点链表
	}
	/* (2)插入位置不在首位的情况 */
	priorNode=findKth(p-1,list);// 获取要插入位置的前一个结点的信息,priorNode结点是用来临时保存查找的前一个结点的信息的
	if(priorNode==NULL) { // 判断priorNode是否为NULL
		return NULL;
	} else {
		newNode=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点
		newNode->data=data; // 指定新结点的数据
		newNode->next=priorNode->next; // 插入结点的下一个结点的地址就是原前一个结点priorNode的下一个结点priorNode->next
		priorNode->next=newNode;// 将原前一个结点priorNode的下一个结点地址指向新结点newNode
		return list;// 返回插入成功单链表
	}
}

/* 删除单链表中指定序号位置的结点 */
/* p指的是要删除结点的序号位置;*list指的是要执行删除操作的单链表 */
LNode * deleteNode(int p,LNode *list) {
	LNode *temp,*priorNode;
	/* 分情况讨论:(1)删除位置在首位;(2)删除位置不在首位 */
	/* (1)删除位置在首位的情况 */
	if(p==1) {
		temp=list; // 临时保存第一个结点的地址
		if(list==NULL) { // 判断要删除的第一个结点是否为NULL
			return NULL;
		} else {
			list=list->next; // 将开始结点指向第一个结点的下一个结点,达到了删除的目的
		}
		free(temp); // 释放结点资源
		return list; // 返回删除成功的单链表
	}
	/* (2)删除位置不在首位的情况 */
	priorNode=findKth(p-1,list); // 获取要被删除结点的前一个结点
	if(priorNode==NULL) { // 判断前一个结点是否存在
		printf("第%d个结点不存在!",p-1);
		return NULL; // 判断要被删除的结点是否存在
	} else if(priorNode->next==NULL) {
		printf("第%d个结点不存在!",p);
		return NULL;
	} else {
		temp=priorNode->next; // 临时保存要删除的结点
		priorNode->next=temp->next;// 将前一个结点priorNode的下一个结点的地址指向被删除结点的下一个结点
		free(temp);// 释放结点资源
		return list;
	}
}

int main() {
	/* [0.]创建初始测试单链表 */
	LNode *list,*t1,*t2,*t3,*t4;
	LNode *list2,*mergeList;
	list=createList();// 创建测试链表
	printList(list);// 打印单链表

	/* [1.]单链表的表长 */
	printf("\n[1.]%d\n",length(list));

	/* [2.]按序号查找结点 */
	t1=findKth(3,list); // 在list单链表中查找序号为3的结点
	printf("\n[2.]%d\n",t1->data);// 打印输出该结点的数据data

	/* [3.]按值查找结点 */
	t2=find(2,list); // 在list单链表中查找data为2的结点
	printf("\n[3.]%d\n",t2->data);// 打印输出该节点的数据data

	/* [4.]插入新结点 */
	t3=insert(99,3,list); // 在list单链表中的序号为3的位置插入data为99的结点
	printList(t3); // 打印当前链表

	/* [5.]删除结点 */
	t4=deleteNode(4,list);// 删除list单链表中序号为4的结点
	printList(t4);// 打印删除后的list

	return 0;
}

测试结果

算法总结

求单链表的表长的思路是:

  • 使用计数器统计有多少个有效的元素结点。

求单链表的表长的核心代码是:

/* 求单链表的表长的算法 */
/* *list指的是单链表 */
int length(LNode *list) {
	LNode *temp=list;// 临时指针变量
	int i=0; // 计数器,用来统计表长,初始长度为0
	while(temp!=NULL) { // 循环条件,当单链表中结点不为NULL
		temp=temp->next; // 指向下一个结点
		i++; // 计数器加1
	}
	return i;
}

按序号查找操作的思路是:

  • 通过一个计数器来对比是否等于所给序号
  • 如果相等则返回当前的结点链表

按序号查找操作的核心代码是:

/* 按序号查找的算法:findKth */
/* num表示结点的序号(不是下标);*list指的是要进行查找操作的单链表 */
LNode * findKth(int num,LNode *list) {
	LNode *temp=list;
	int i=1;// 计数器,统计当前是第几个结点
	while(temp!=NULL&&i<num) { // 循环遍历结点,判断链表是否为空并且计数器小于查找查找序号(退出while循环后,i==K)
		i+=1; // 计数器加1
		temp=temp->next; // 指向下一个结点
	}
	if(i==num) { // 判断是否查找到序号
		return temp;// 返回查找到的结点
	} else {
		return NULL;
	}
}

按值查找操作的思路是:

  • 将要查找的值与链表中每个元素的结点的值相比较
  • 如果相等则返回

按值查找操作的核心代码是:

/* 按值查找算法:find */
/* data指要查找的data;*list指的是要在哪个链表中查询 */
LNode * find(int data,LNode *list) {
	LNode *temp=list;
	while(temp!=NULL&&temp->data!=data) { // 遍历循环,当temp->data=x时跳出循环,temp即为所求
		temp=temp->next;
	}
	return temp;
}

在单链表中插入新结点的思路是:

  • 分情况讨论,插入位置是否在首位
  • 如果在首位,则创建新结点,将新结点的下一个结点地址指向原链表的头结点,再返回新结点即可
  • 如果不在首位,则获取插入位置的前一个位置的结点,然后创建新结点进行连接

在单链表中插入新结点的核心代码是:

/* 在单链表中插入新结点 */
/* data指的是要插入的数据;p指的是要插入的结点序号位置(不是下标);*list指的是要插入的单链表 */
LNode * insert(int data,int p,LNode *list) {
	LNode *newNode,*priorNode;
	/* 分情况讨论:(1)插入位置在首位;(2)插入位置不在首位 */
	/* (1)插入位置在首位的情况 */
	if(p==1) { // 如果要插入的位置是第一个结点
		newNode=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点
		newNode->data=data; // 指定新结点的数据
		newNode->next=list; // 指定新结点的下一个结点的地址(如果是插入在首位,那么下一个结点的地址就是链表list的第一个结点的地址)
		return newNode; // 返回结点链表
	}
	/* (2)插入位置不在首位的情况 */
	priorNode=findKth(p-1,list);// 获取要插入位置的前一个结点的信息,priorNode结点是用来临时保存查找的前一个结点的信息的
	if(priorNode==NULL) { // 判断priorNode是否为NULL
		return NULL;
	} else {
		newNode=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点
		newNode->data=data; // 指定新结点的数据
		newNode->next=priorNode->next; // 插入结点的下一个结点的地址就是原前一个结点priorNode的下一个结点priorNode->next
		priorNode->next=newNode;// 将原前一个结点priorNode的下一个结点地址指向新结点newNode
		return list;// 返回插入成功单链表
	}
}

删除单链表中指定序号位置结点操作的思路是:

  • 也分情况讨论,删除位置是否是首位
  • 如果是首位,则将头结点指向原头结点的下一个结点,然后使用free()是否资源。
  • 如果不在首位,则通过findKth()方法获取要删除结点的前一个结点,然后更改结点的下一个结点指向

删除单链表中指定序号位置结点操作的核心代码是:

/* 删除单链表中指定序号位置的结点 */
/* p指的是要删除结点的序号位置;*list指的是要执行删除操作的单链表 */
LNode * deleteNode(int p,LNode *list) {
	LNode *temp,*priorNode;
	/* 分情况讨论:(1)删除位置在首位;(2)删除位置不在首位 */
	/* (1)删除位置在首位的情况 */
	if(p==1) {
		temp=list; // 临时保存第一个结点的地址
		if(list==NULL) { // 判断要删除的第一个结点是否为NULL
			return NULL;
		} else {
			list=list->next; // 将开始结点指向第一个结点的下一个结点,达到了删除的目的
		}
		free(temp); // 释放结点资源
		return list; // 返回删除成功的单链表
	}
	/* (2)删除位置不在首位的情况 */
	priorNode=findKth(p-1,list); // 获取要被删除结点的前一个结点
	if(priorNode==NULL) { // 判断前一个结点是否存在
		printf("第%d个结点不存在!",p-1);
		return NULL; // 判断要被删除的结点是否存在
	} else if(priorNode->next==NULL) {
		printf("第%d个结点不存在!",p);
		return NULL;
	} else {
		temp=priorNode->next; // 临时保存要删除的结点
		priorNode->next=temp->next;// 将前一个结点priorNode的下一个结点的地址指向被删除结点的下一个结点
		free(temp);// 释放结点资源
		return list;
	}
}

 

单链表操作总结

思路

  • 求单链表的表长的思路是:
    • 使用计数器统计有多少个有效的元素结点。
  • 按序号查找操作的思路是:
    • 通过一个计数器来对比是否等于所给序号
    • 如果相等则返回当前的结点链表
  • 按值查找操作的思路是:
    • 将要查找的值与链表中每个元素的结点的值相比较
    • 如果相等则返回
  • 在单链表中插入新结点的思路是:
    • 分情况讨论,插入位置是否在首位
    • 如果在首位,则创建新结点,将新结点的下一个结点地址指向原链表的头结点,再返回新结点即可
    • 如果不在首位,则获取插入位置的前一个位置的结点,然后创建新结点进行连接
  • 删除单链表中指定序号位置结点操作的思路是:
    • 也分情况讨论,删除位置是否是首位
    • 如果是首位,则将头结点指向原头结点的下一个结点,然后使用free()是否资源。
    • 如果不在首位,则通过findKth()方法获取要删除结点的前一个结点,然后更改结点的下一个结点指向

代码

/* 归并递增:将两个递增单链表归并成一个递增单链表(不带头节点),返回合并后的递增单链表 */
/* *A指的是递增单链表A;*B指的是递增单链表B;*&C是带头结点的单链表 */
void * merge(LNode *A,LNode *B,LNode *&C) {
	LNode *p;
	LNode *q;
	p=A;// 注意:这里A传入的是不带头节点的单链表,如果A是带头结点的单链表,那么这里应该是p=A->next
	q=B;// 注意:这里B传入的是不带头节点的单链表,如果B是带头结点的单链表,那么这里应该是p=B->next
	/* 创建一个带头节点的单链表start */
	LNode *temp;
	C=(LNode *)malloc(sizeof(LNode));// 创建一个带头节点的链表
	C->next=NULL;// 设置头节点的下一个结点为NULL
	temp=C;// 临时保存头节点,也是尾结点,参与下面的结点连接操作
	// (temp既相当于链表的头节点,又相当于链表的尾结点,完成的链表就是head->next)
	/* 创建一个带头节点的单链表end */
	while(p!=NULL&&q!=NULL) { // 在同时满足p和q链表不为NULL的情况下循环
		// 判断p链表中结点的数据是否小于等于q链表中的数据
		if(p->data<=q->data) { // 如果p->data比q->data小于等于,那么
			// 体现了尾插法的思想
			temp->next=p; // 将临时头节点的下一个结点指向p
			p=p->next;// 并且p指向下一个结点
			temp=temp->next; // 并且temp也指向终端结点
		} else { // 反之,如果大,那么将q链表中的该结点添加到temp尾
			temp->next=q;
			q=q->next;
			temp=temp->next;
		}
	}
	if(p!=NULL) { // 处理q链表已经全部插入到新链表,而p链表还剩下些结点,那么将这些结点链接到temp尾
		temp->next=p;
	}
	if(q!=NULL) { // 同上
		temp->next=q;
	}
}

/* 归并递减:将两个递增单链表归并成一个递减的单链表,返回一个递减的单链表 */
/* *A指的是递增单链表A;*B指的是递增单链表B */
LNode * mergeDe(LNode *A,LNode *B) {
	LNode *C,*temp,*p;
	LNode *a=A,*b=B;
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	while(a!=NULL&&b!=NULL) {
		if(a->data<=b->data) {
			// 为什么需要使用临时结点p?因为p要作为插入的新结点使用又要指向终端结点,不能混合交叉使用,所以需要一个临时结点来保存
			p=a; // 临时保存a结点,即新要插入到链表头部的新结点
			a=a->next; // 到a的下一个结点
			p->next=temp->next;  // 头插法的经典使用
			temp->next=p;
		} else {
			p=b;
			b=b->next;
			p->next=temp->next;
			temp->next=p;
		}
	}
	while(a!=NULL) {
		p=a;
		a=a->next;
		p->next=temp->next;
		temp->next=p;
	}
	while(b!=NULL) {
		p=b;
		b=b->next;
		p->next=temp->next;
		temp->next=p;
	}
	return C->next;
}

/* 使用尾插法建立单链表,即新添加的元素添加到链表的尾部 */
/* *&C指的是建立的单链表;a[]指的是批量添加的元素数组;n表示数组长度 */
void createListL(LNode *&C,int a[],int n) {
	LNode *temp,*node;
	// 创建一个带头节点的单链表
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	// 循环遍历数组,批量为链表插入数据
	for(int i=0; i<n; i++) { // 循环申请n个结点来接收a中的元素
		node=(LNode *)malloc(sizeof(LNode)); // 创建新结点
		node->data=a[i]; // 为新结点赋予数据
		temp->next=node; // 将原尾结点的下一个结点指向新结点
		temp=temp->next; // 指向终端结点,以便接收下一个结点
	}
	temp->next=NULL; // 所有元素已经填装完成,将终端结点置为NULL
}

/* 使用头插法建立单链表,即新添加的元素添加到链表的头部 */
/* *&C指的是建立的单链表;a[]指的是批量添加的元素数组;n表示数组长度 */
void createListF(LNode *&C,int a[],int n) {
	LNode *temp,*node;
	// 创建带头节点的单链表
	C=(LNode *)malloc(sizeof(LNode));
	C->next=NULL;
	temp=C;
	for(int i=0; i<n; i++) {
		node=(LNode *)malloc(sizeof(LNode));// 新建结点
		node->data=a[i];// 为新结点赋值
		// 注意:下面两行不能交换位置,否则会进入死循环
		node->next=temp->next; // node所指新结点的指针域next指向C的开始结点
		temp->next=node; // 头结点的指针域next指向node,使得node成为新的开始结点
	}
}

/* 打印单链表 */
/* *list指的是要被打印输出的单链表 */
void printList(LNode *list) {
	printf("\n");
	while(list!=NULL) { // 循环单链表
		printf("%ld\t",list->data); // 打印单链表中的data数据
		list=list->next; // 遍历至下一个结点
	}
	printf("\n"); // 换行
}

/* 求单链表的表长的算法 */
/* *list指的是单链表 */
int length(LNode *list) {
	LNode *temp=list;// 临时指针变量
	int i=0; // 计数器,用来统计表长,初始长度为0
	while(temp!=NULL) { // 循环条件,当单链表中结点不为NULL
		temp=temp->next; // 指向下一个结点
		i++; // 计数器加1
	}
	return i;
}

/* 按序号查找的算法:findKth */
/* num表示结点的序号(不是下标);*list指的是要进行查找操作的单链表 */
LNode * findKth(int num,LNode *list) {
	LNode *temp=list;
	int i=1;// 计数器,统计当前是第几个结点
	while(temp!=NULL&&i<num) { // 循环遍历结点,判断链表是否为空并且计数器小于查找查找序号(退出while循环后,i==K)
		i+=1; // 计数器加1
		temp=temp->next; // 指向下一个结点
	}
	if(i==num) { // 判断是否查找到序号
		return temp;// 返回查找到的结点
	} else {
		return NULL;
	}
}

/* 按值查找算法:find */
/* data指要查找的data;*list指的是要在哪个链表中查询 */
LNode * find(int data,LNode *list) {
	LNode *temp=list;
	while(temp!=NULL&&temp->data!=data) { // 遍历循环,当temp->data=x时跳出循环,temp即为所求
		temp=temp->next;
	}
	return temp;
}

/* 在单链表中插入新结点 */
/* data指的是要插入的数据;p指的是要插入的结点序号位置(不是下标);*list指的是要插入的单链表 */
LNode * insert(int data,int p,LNode *list) {
	LNode *newNode,*priorNode;
	/* 分情况讨论:(1)插入位置在首位;(2)插入位置不在首位 */
	/* (1)插入位置在首位的情况 */
	if(p==1) { // 如果要插入的位置是第一个结点
		newNode=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点
		newNode->data=data; // 指定新结点的数据
		newNode->next=list; // 指定新结点的下一个结点的地址(如果是插入在首位,那么下一个结点的地址就是链表list的第一个结点的地址)
		return newNode; // 返回结点链表
	}
	/* (2)插入位置不在首位的情况 */
	priorNode=findKth(p-1,list);// 获取要插入位置的前一个结点的信息,priorNode结点是用来临时保存查找的前一个结点的信息的
	if(priorNode==NULL) { // 判断priorNode是否为NULL
		return NULL;
	} else {
		newNode=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点
		newNode->data=data; // 指定新结点的数据
		newNode->next=priorNode->next; // 插入结点的下一个结点的地址就是原前一个结点priorNode的下一个结点priorNode->next
		priorNode->next=newNode;// 将原前一个结点priorNode的下一个结点地址指向新结点newNode
		return list;// 返回插入成功单链表
	}
}

/* 删除单链表中指定序号位置的结点 */
/* p指的是要删除结点的序号位置;*list指的是要执行删除操作的单链表 */
LNode * deleteNode(int p,LNode *list) {
	LNode *temp,*priorNode;
	/* 分情况讨论:(1)删除位置在首位;(2)删除位置不在首位 */
	/* (1)删除位置在首位的情况 */
	if(p==1) {
		temp=list; // 临时保存第一个结点的地址
		if(list==NULL) { // 判断要删除的第一个结点是否为NULL
			return NULL;
		} else {
			list=list->next; // 将开始结点指向第一个结点的下一个结点,达到了删除的目的
		}
		free(temp); // 释放结点资源
		return list; // 返回删除成功的单链表
	}
	/* (2)删除位置不在首位的情况 */
	priorNode=findKth(p-1,list); // 获取要被删除结点的前一个结点
	if(priorNode==NULL) { // 判断前一个结点是否存在
		printf("第%d个结点不存在!",p-1);
		return NULL; // 判断要被删除的结点是否存在
	} else if(priorNode->next==NULL) {
		printf("第%d个结点不存在!",p);
		return NULL;
	} else {
		temp=priorNode->next; // 临时保存要删除的结点
		priorNode->next=temp->next;// 将前一个结点priorNode的下一个结点的地址指向被删除结点的下一个结点
		free(temp);// 释放结点资源
		return list;
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值