王道数据结构【链表】部分代码实现(C语言)

数据结构专栏:

  1. 王道数据结构【顺序表】部分代码实现(C语言)
  2. 王道数据结构【链表】部分代码实现(C语言)
  3. 王道数据结构【栈队列及其应用】部分代码实现(C语言)
  4. 王道数据结构【二叉树】部分代码实现(C语言)
  5. 王道数据结构【图】部分代码实现(C语言)
  6. 王道数据结构【查找排序】部分代码实现(C语言)

递归删除不带头结点链表中指定值

/*
	设计一个递归算法,删除一个不带头结点的单链表中所有值为x的节点
	分析:
		首先我们要创建单链表,并赋值,然后递归去判断值,进行删除
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
struct Link
{
	int value;
	struct Link *next;
};
void deleteX(Link *&p, int delNum) {//这里的第一个函数参数必须是引用值,不然会导致断链
	struct Link *pre;//定义一个指针,进行删除
	if (p == NULL) return;
	if (p->value == delNum) {
		pre = p;
		p = p->next;
		free(pre);
		deleteX(p, delNum);
	}
	else
		deleteX(p->next, delNum);
}
Link *create() {
	struct Link *p, *rear, *head;
	head = (struct Link *)malloc(sizeof(struct Link));
	rear = (struct Link *)malloc(sizeof(struct Link));
	head = NULL;
	rear = NULL;
	int value;
	printf("请输入链表各节点的值,以9999结束:");
	scanf("%d", &value);
	while (value != 9999) {//依次创建节点
		p = (struct Link *)malloc(sizeof(struct Link));//创建一个新的节点
		p->value = value;
		p->next = NULL;
		if (head == NULL) {
			rear = p;
			head = p;
		}
		else {
			rear->next = p;
			rear = p;
		}
		scanf("%d", &value);
	}
	rear->next = NULL;
	return head;
}
Link *create2() {
	struct Link *p, *rear, *head;
	head = (struct Link *)malloc(sizeof(struct Link));
	head = NULL;
	int value;
	printf("请输入链表各节点的值,以9999结束:");
	scanf("%d", &value);
	while (value != 9999) {//依次创建节点
		p = (struct Link *)malloc(sizeof(struct Link));
		p->value = value;
		p->next = NULL;
		if (head == NULL) {
			head = p;
		}
		else {

			p->next = head->next;
			head->next = p;
		}

		scanf("%d", &value);
	}
	return head;
}
int main() {
	int delNum;
	struct Link *head, *q;
	head = create();
	q = head;
	printf("打印链表:");
	while (q != NULL) {
		printf("%d ", q->value);
		q = q->next;
	}
	q = head;
	printf("请输入想要删除的节点的值:");
	scanf("%d", &delNum);
	deleteX(q, delNum);
	printf("删除后链表:");
	while (q != NULL) {
		printf("%d ", q->value);
		q = q->next;
	}
	return 0;
}

带头结点链表删除指定值

/*
	在带头结点的单链表L中,删除所有值为X的节点,并释放其空间,假设值为X的节点不唯一
	分析:
		和上题相似,只是多了一个头结点。
*/
struct Link {

	int data;
	struct Link *next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void deleteX(Link *p,int delNum) {
	/*struct Link* q;//这是递归方法
	if (p == NULL) return;
	if (p->data==delNum) {
		q = p;
		p = p->next;
		free(q);
		deleteX(p,delNum);
	}
	else {
		deleteX(p->next, delNum);
	}*/
	//不采取递归,直接遍历
	struct Link *pre = p, *q = p->next,*r;
	while (q) {
		if (q->data==delNum) {
			r = q;//r指向待删除节点
			q = q->next;//
			pre->next = q;//删除节点
			free(r);//释放节点
		}
		else {
			pre = q;
			q = q->next;
			
		}
	}
}

int main() {
	//创建节点
	struct Link *head = (struct Link*)malloc(sizeof(struct Link));
	struct Link *q = (struct Link*)malloc(sizeof(struct Link));
	q = head;
	head->next = NULL;
	int n,data,delNum;
	printf("请输入节点个数:");
	scanf("%d",&n);
	for (int i = 0; i < n;i++) {
		printf("请输入第%d个节点值:",i+1);
		struct Link *p = (struct Link*)malloc(sizeof(struct Link));
		scanf("%d",&data);
		p->data = data;
		head->next = p;
		head = p;
	
	}
	head->next = NULL;//这里要将指针的next指向NULL,不然后面的判断会出问题,而且这也是应该养成的好习惯
	head = q;//head回到头结点
	printf("当前链表值为:");
	while (head->next) {
		printf("%d ",head->next->data);
		head = head->next;
	}
	printf("\n");
	printf("请输入要删除的值:");
	scanf("%d",&delNum);
	head = q;//head回到头结点
	deleteX(head,delNum);
	printf("删除后链表值为:");
	while (head->next) {
		printf("%d ", head->next->data);
		head = head->next;
	}
	return 0;
}

反向输出链表值

/*
	有一带头结点的链表,设计一算法从尾到头的输出每个节点的值。
	分析:
		这种类型就有点像是栈的性质,我们可以利用递归来处理,出口便是尾元素
*/
struct Link {
	int data;
	struct Link* next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void reverseOutput(Link *p) {
	if (p == NULL) return;
	else {
		reverseOutput(p->next);
		printf("%d ",p->data);
	}
	
}
int main() {
	int n,data;
	printf("请输入创建链表的节点个数:");
	scanf("%d",&n);
	struct Link *q;
	struct Link *head =(struct Link*) malloc(sizeof(struct Link));
	head->next = NULL;
	q = head;
	for (int i = 0; i < n;i++) {
		struct Link *newP = (struct Link*) malloc(sizeof(struct Link));
		printf("请输入第一个节点的值:");
		scanf("%d",&data);
		newP->data = data;
		newP->next = NULL;
		head->next = newP;
		head = head->next;//head要始终指向最新节点
	}
	head->next = NULL;
	head = q;//最后head要指向头结点
	reverseOutput(head->next);
	return 0;
}

带头结点的单链表删除最小值节点

/*
	删除链表中唯一的最小值
	分析:
		目前能想到的就是遍历整个链表,记录下最小节点的指针,然后进行删除,时间复杂度为O(n).
*/
struct Link {
	int data;
	struct Link* next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void deleteMin(Link *p) {
	struct Link *preMinp = p, *minP = p->next,*preQ=p->next, *q = p->next->next,*f;
	while (q) {
		if (q->data < minP->data) {//如果比当前值更小
			minP = q;//更换
			preMinp = preQ;//前驱一起更换
		}
		preQ = q;//继续前进
		q = q->next;
	}
	f = minP;
	preMinp->next = minP->next;//删除
	free(f);//释放

}
int main() {
		int n,data;
		printf("请输入创建链表的节点个数:");
		scanf("%d",&n);
		struct Link *q;
		struct Link *head =(struct Link*) malloc(sizeof(struct Link));
		head->next = NULL;
		q = head;
		for (int i = 0; i < n;i++) {
			struct Link *newP = (struct Link*) malloc(sizeof(struct Link));
			printf("请输入第一个节点的值:");
			scanf("%d",&data);
			newP->data = data;
			newP->next = NULL;
			head->next = newP;
			head = head->next;//head要始终指向最新节点
		}
		head->next = NULL;
		head = q;//最后head要指向头结点
		printf("打印链表:");
		while (head->next) {
			printf("%d ", head->next->data);
			head = head->next;
		}
		head = q;
		deleteMin(head);
		printf("删除后链表值为:");
		while (head->next) {
			printf("%d ", head->next->data);
			head = head->next;
		}
		return 0;
}

带头结点单链表就地逆置

/*
	就地逆置链表
	分析:
		我们采用头插法进行逆置。
*/
struct Link {
	int data;
	struct Link* next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
//void reverse(Link *h) {
	//struct Link *pre = h, *p = h->next, *q = h->next,*r;
	pre记录操作节点p上一个节点,q记录第一个节点,之后需要指向NULL,r用于指向每一次操作时p的后一个节点,防止断链
	//while (p) {//遍历操作,修改指针指向
	//	r = p->next;
	//	p->next = pre;
	//	pre = p;
	//	p = r;
	//}
	//h->next = pre;//头指针指向最后一个节点
	//q->next = NULL;//第一个节点指向NULL,不然就是循环单链表了
	

	//我们也可以采用头插法进行逆置,两个方法的时间复杂度均为O(N)
	struct Link *l = h, *p = h->next,*r;
	l->next = NULL;
	while (p) {
		r = p->next;
		p->next = l->next;
		l->next = p;
		p = r;
	}
	h = l;
}
int main() {
	struct Link *head = (struct Link*) malloc(sizeof(struct Link));
	Link *createLink(int);
	head = createLink(0);
	reverse(head);
	printf("逆置后链表值为:");
	while (head->next) {
		printf("%d ", head->next->data);
		head = head->next;
	}
	return 0;
}

单链表排序

/*
	有一个带头结点的单链表L,设计一个算法使其递增有序
	分析:
		我们可以采用冒泡排序对其操作,使其递增有序,时间复杂度为O(n^2)。
*/

struct Link {
	int data;
	struct Link *next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void bubbleSort(Link *h) {//冒泡排序
	int flag = 0;//排序标志,产生过变动就置为1
	int count = 0;//记录链表长度
	struct Link *pre=h, *p = h->next,*r;
	while (p) {
		count++;
		p = p->next;
	}
	p = h->next;
	for (int i = 0; i < count;i++) {
		flag = 0;
		while (p->next) {//开始第i+1轮冒泡
			if (p->data > p->next->data) {//前者大于后者,则需要交换
				r = p->next->next;//r指向下一个节点,防止断链
				pre->next = p->next;
				p->next->next = p;
				pre = p->next;
				p->next = r;
				flag = 1;//有改动,flag置为1
			}
			else {
				pre = p;
				p = p->next;
			}
		}
		if (!flag) break;//若某一轮链表未作变换,则认定已经排好序,退出循环
		pre = h;//重新从头开始遍历
		p = h->next;
	}
}
int main() {
	Link* createLink(int);
	struct Link *head;
	head = createLink(0);
	bubbleSort(head);
	printf("排序后链表值为:");
	while (head->next) {
		printf("%d ", head->next->data);
		head = head->next;
	}
	return 0;
}

删除指定范围值

/*
	设一个带头结点的单链表所有元素的数据值无序,试编写函数删除表中介于给定的两个值(作为函数参数给出)之间的元素。
	分析:
		分别设置pre,p,r指针,遍历,符合条件便进行删除。
*/
struct Link {
	int data;
	struct Link *next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void deleteNum(Link *h,int min,int max) {
	struct Link *pre = h, *p = h->next, *r;
	while (p) {
		if (p->data>min&&p->data<max) {//符合条件,进行删除
			r = p->next;
			pre->next = p->next;
			free(p);
			p = r;
		}
		else {
			pre = p;
			p = p->next;
		}
	}
}
int main() {
	int min, max;
	struct Link*head;
	Link *createLink(int);//创建链表的代码我单独封装了一个文件
	void printfNowLink(Link*);
	head = createLink(0);
	printf("请输入要删除的值所在的范围:\n");
	printf("min=");
	scanf("%d",&min);
	printf("max=");
	scanf("%d", &max);
	deleteNum(head,min,max);
	printfNowLink(head);
	return 0;
}

递增输出并删除

/*
	按递增次序输出单链表各节点的数据元素,并释放节点所占的存储空间。
	分析:
		我们可以先进行排序,然后依次输出,并释放节点空间,我们也可以直接进行遍历,找到目前最小值进行输出,然后释放,注意不要断链
		我们这里采取第二种方式
*/
struct Link {
	int data;
	struct Link *next;
};
#include <stdio.h>
#include <stdlib.h>
void printAndDel(Link *h) {
	struct Link *pre = h, *p = h->next, *r,*min=h->next,*minPre=h;//为了操作的顺利进行,我们需要时刻保存节点的前驱与后继
	int count = 0;
	while (p) {
		count++;
		p = p->next;
	}
	p = h->next;
	printf("输出顺序为:\n");
	for (int i = 0; i < count;i++) {
		while (p) {
			if (p->data < min->data) {
				minPre = pre;
				min = p;
			}
			pre = p;
			p = p->next;
		}
		printf("%d ",min->data);
		r = min->next;
		minPre->next = min->next;
		free(min);
		pre = minPre = h;
		p = min = h->next;
	}
	printf("\n");
}
int main() {
	struct Link *head;
	Link *createLink(int);
	void printfNowLink(Link*);
	head = createLink(0);
	printAndDel(head);
	printfNowLink(head);
	return 0;
}

按奇偶拆分成单链表

/*
	将一个带头结点单链表A分解成两个带头结点的单链表A和B,使得A中含有原表中序号为奇数的元素,B中为偶数,且保持其相对位置不变
	分析:
		首先我们需要分配一个节点空间为B作为头节点,然后设置一个flag,为0时认为是奇数,链给A,为1时认为是
		偶数,连给B
*/
struct Link {
	int data;
	struct Link *next;
};
#include <stdio.h>
#include <stdlib.h>
void divide(Link *lb,Link *la) {
	int flag = 0;//做奇偶判定,因为奇数之后一定是偶数,偶数之后一定是奇数
	struct Link *l=la, *p = la->next,*rb=lb,*ra=l;
	l->next = NULL;//原链表头结点置空
	while (p) {//要使其顺序不变,采用尾插法
		if (!flag) {
			ra->next = p;
			ra = p;
			flag = 1;
		}
		else {
			rb->next = p;
			rb = p;
			flag = 0;
		}
		p = p->next;
	}
	ra->next = NULL;//要记得将末尾节点的指针指向NULL,不然就仍然是之前的指针,导致结果不正确
	rb->next = NULL;
}
int main() {
	struct Link *head;
	Link *createLink(int);
	void printfNowLink(Link *);
	head = createLink(0);
	struct Link *b = (struct Link*)malloc(sizeof(struct Link));//开辟节点空间
	divide(b,head);
	printfNowLink(b);
	printf("\n");
	printfNowLink(head);
	return 0;
}

有序链表去重

/*
	在一个递增有序的线性表中,有数值相同的元素存在,若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再有重复的元素。
	分析:
		对于这类去重,我们肯定需要进行遍历,而这道题给我们的是递增有序的序列,我们便可以进行一一比较,后一个元素与当前元素相同
		时便删除当前元素
*/
struct Link {
	int data;
	struct Link *next;
};

#include <stdio.h>
#include <stdlib.h>
void deleteRep(Link *h) {
	struct Link *pre = h, *p = h->next, *r;//删除节点必备三剑客
	while (p->next) {
		if (p->data==p->next->data) {//当前节点与后续节点值相同,则删除并释放空间
			r = p->next;//必须先保存后续节点,防止断链
			pre->next = p->next;
			free(p);
			p = r;
			continue;
		}
		pre = p;
		p = p->next;
	}
}
int main() {
	struct Link * head;
	Link *createLink(int);
	void printfNowLink(Link *);
	head = createLink(0);
	deleteRep(head);
	printfNowLink(head);
	return 0;
}

归并单链表

/*
	假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素递减的单链表,
	并要求利用原来两个单链表的节点存放归并后的单链表。
	分析:
		因为链表递增,所以我们可以采用头插法进行处理,以a链为起始链,进行归并
*/
struct Link {
	int data;
	struct Link *next;
};
#include <stdio.h>

void merge(Link *ha,Link *hb) {
	struct Link *l = ha, *pa = ha->next, *pb = hb->next, *ra, *rb;
	l->next = NULL;
	while (pa&&pb) {
		if (pa->data<pb->data) {
			ra = pa->next;
			pa->next = l->next;
			l->next = pa;
			pa = ra;
		}
		else {
			rb = pb->next;
			pb->next = l->next;
			l->next = pb;
			pb = rb;
		}
	}
	while (pa) {
		ra = pa->next;
		pa->next = l->next;
		l->next = pa;
		pa = ra;
	}
	while (pb) {
		rb = pb->next;
		pb->next = l->next;
		l->next = pb;
		pb = rb;
	}
}
int main() {
	struct Link *ha,*hb;
	Link *createLink(int);
	void printfNowLink(Link *);
	ha = createLink(0);
	hb = createLink(0);
	merge(ha,hb);
	printfNowLink(ha);
	return 0;
}

提取公共元素

/*
		设A、B是两个单链表(带头结点),其中元素递增有序,设计一个算法从A、B中的公共元素产生单链表C,要求不破坏A、B节点
		分析:
			要求不破坏A、B节点,故我们需要重新创建分配节点空间,来进行连接。为寻找公共元素,每遍历A中的一个元素,都去
			与B中元素一一比较,同则取值给另一空间节点,连接到C上。时间复杂度为O(n^2)。
			因为这两个链表是递增有序的,那么我们可以设置两个指针同步比较,相同则加入c,不同小的那个往后移,直至到链表末尾
			这样的时间复杂度为O(n).
*/
struct Link {
	int data;
	struct Link *next;
};
#include <stdlib.h>
#include <stdio.h>
void linkCommon(Link *a, Link *b, Link *c ) {
	struct Link *lc = c,*la=a->next,*lb=b->next,*rc=lc;
	while (la) {
		while (lb) {
			if (la->data==lb->data) {//如果是公共元素
				struct Link *p = (struct Link*)malloc(sizeof(struct Link));
				p->data = la->data;
				rc->next = p;//采用尾插法
				rc = p;
				break;
			}
			lb = lb->next;
		}
		la = la->next;
		lb = b->next;
	}
	rc->next = NULL;//最后一个节点指针需要指向NULL。
}
void listCommon(Link *a,Link *b,Link *c) {
	struct Link *rc = c, *la = a->next, *lb = b->next;
	while (la&&lb) {
		if (la->data==lb->data) {
			struct Link *p = (struct Link*)malloc(sizeof(struct Link));
			p->data = la->data;
			p->next = NULL;
			rc->next = p;
			rc = p;
			la = la->next;
			lb = lb->next;
		}
		else {
			la->data < lb->data ? la = la->next : lb = lb->next;
		}
	}
	rc->next = NULL;
}
int main() {
	struct Link *a, *b;
	Link *createLink(int);
	void printfNowLink(Link *);
	a = createLink(0);
	b = createLink(0);
	struct Link *c = (struct Link*)malloc(sizeof(struct Link));
	c->next = NULL;
	//linkCommon(a,b,c);
	listCommon(a,b,c);
	printfNowLink(c);
	return 0;
}

求两链表交集

/*
	已知两个链表A、B分别表示两个集合,其元素递增排列,编制函数,求A与B的交集,并存放于A链表中。
	分析:
		与上题类似,因为链表本身递增排序,我们可以设置两个指针,同时遍历A、B链表,同则保留,异则删除
*/
struct Link {
	int data;
	struct Link *next;
};

#include <stdio.h>
void listCommon(Link *a,Link *b) {
	struct Link  *pA = a->next, *pB = b->next,*r,*la=a;//用la指向a,便可直接链在a后面
	la->next = NULL;
	while (pA&&pB) {
		if (pA->data==pB->data) {
			la->next = pA;
			la = pA;
			pA = pA->next;
			pB = pB->next;
		}
		else {
			pA->data < pB->data ? pA = pA->next : pB = pB->next;
		}
	}
	la->next = NULL;
}
int main() {
	struct Link *a, *b;
	Link *createLink(int);
	void printfNowLink(Link *);
	a = createLink(0);
	b = createLink(0);
	listCommon(a,b);
	printfNowLink(a);
	return 0;
}

判断是否是子序列

/*
	存在两个单链表序列A、B,设计函数判断B是否为A的子序列。
	分析:
		最直接的方法:循环遍历,从A链的第一个元素开始与B链对比,如遇见不同,从A链下一个又开始,直至到达链尾
*/
struct Link {
	int data;
	struct Link *next;

};

#include <stdio.h>

void subList(Link *a,Link *b) {
	struct Link *la = a ,*pA = la->next, *pB = b->next;
	while (pA&&pB) {
		if (pA->data==pB->data) {//相等继续对比下一个
			pA = pA->next;
			pB = pB->next;
		}
		else {
			pB = b->next;//pb从头开始与pa对比
			la = la->next;//失败一次,la往后移动一个节点
			pA = la->next;//pa从下一个节点又开始
		}
	}
	pB == NULL ? printf("true") : printf("false");//如果pb为NULL,说明已比对完成
	
}
int main() {
	struct Link *a, *b;
	Link *createLink(int);
	a = createLink(0);
	b = createLink(0);
	subList(a,b);
	return 0;
}

判断循环双链表是否对称

// createDouLoopLink.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
struct Link {
	int data;
	struct Link *next;
	struct Link *pre;
};
Link *createDouLoopLink() {
	int n, data;
	struct Link *head = (struct Link *)malloc(sizeof(struct Link));
	head->next = NULL;
	head->pre = NULL;
	struct Link *p = head;
	printf("请输入节点个数:n=");
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		printf("请输入第%d个节点值:", i + 1);
		scanf("%d", &data);
		struct Link *newP = (struct Link*)malloc(sizeof(struct Link));
		newP->data = data;
		newP->pre = p;
		p->next = newP;
		p = newP;
	}
	p->next = head;
	head->pre = p;
	return head;
}

将B链表链接到A链表

/*
	有两个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2连接到h1之后,要求连接后的链表仍保持循环链表形式。
	分析:
		首先我们要找到h1的尾结点,找到尾结点后将尾结点的next指向h2的首节点,然后找到h2的尾结点,将其next指针指向h1,
		就大功告成了。
*/
struct Link {
	int data;
	struct Link *next;
};
#include <stdlib.h>
#include <stdio.h>
void linkTwoLists(Link *h1,Link *h2) {
	struct Link *p1 = h1->next, *p2 = h2->next;
	while (p1->next != h1) p1 = p1->next;//这里要去判断p1->next是否等于h1,进而判断出是否到达尾结点
	p1->next = p2;
	while (p2->next != h2) p2 = p2->next;
	p2->next = h1;
	free(h2);//释放h2
}
int main() {
	struct Link *h1, *h2,*p;
	Link *createSinLoopLink();
	h1 = createSinLoopLink();
	h2 = createSinLoopLink();
	linkTwoLists(h1,h2);
	p = h1->next;
	while (p!=h1) {
		printf("%d ",p->data);
		p = p->next;
	}
	return 0;
}

循环输出最小值并删除

/*
	设有一个带头结点的单链表,其节点均为正值,设计一个算法,反复找出单链表中最小值输出并删除,直到单链表为空,最后删除头结点
	分析:
		按照以往的经验,我们需要遍历它,为了保证不断链,我们需要设置pre,p,minPre,min,r等5个指针。
*/
struct Link {
	int data;
	struct Link *next;
};
#include <stdlib.h>
#include <stdio.h>
void inputAndDeleteLink(Link *h) {
	struct Link *pre = h, *minPre = h, *p = h->next, *min = h->next, *r;
	while (h->next!=h) {//如果头结点后面还有值,说明未结束
		while (p!=h) {//寻找最小值
			if (min->data>p->data) {
				minPre = pre;
				min = p;
			}
			pre = p;
			p = p->next;
		}
		printf("%d ",min->data);
		r = min->next;
		minPre->next = min->next;//删除当前最小值
		free(min);//释放节点空间
		p = h->next;//又重头开始遍历
		pre = h;
		minPre = h;
		min = h->next;
	}
	free(h);//最后释放头结点
}
int main() {
	struct Link *head;
	Link *createSinLoopLink();
	head = createSinLoopLink();
	inputAndDeleteLink(head);
	return 0;
}

按频度排序

/*
	给一个非循环双向链表增加一个freq值,用以表示它的访问频率,每访问一次freq就+1,然后需要将该链表按照非增的顺序
	排列,同时最近访问的节点排在相同频度节点的前面,以便使频繁访问的节点总是靠近表头。试编写符合上述要求的Locate(L,x)
	函数,该运算为函数过程,返回找到节点的地址,类型为指针型。

	分析:
		这道题咋一看很唬人,还引入了一个新概念,其实并不难,拆分开来其实就是 查找+排序;我们需要先找到我们要访问的节点
		,更改它的freq值,然后按照freq值的大小从表头寻找插入位置,这样便完成了一次locate操作。
*/
struct Link {
	int data;
	struct Link *pre;
	struct Link *next;
	int freq;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void locate(Link *h,int num) {
	int flag = 0;//找到标志
	struct Link *pre=h, *p = h->next, *t,*preQ=h,*q;
	while (p) {
		if (p->data==num) {//如果找到
			flag = 1;//表示找到
			p->freq++;//freq+1
			t = p;//将该节点保存起来
			if (p->next) {
				pre->next = p->next;//将该节点抠出来
				p->next->pre = pre;
			}
			else {
				pre->next = NULL;//这里也要注意,如果我们查找的节点是最后一个节点,我们要将next指向NULL,不然之后遍历时会出问题
			}
			q = h->next;//这里需要注意,q应该始终指向改变了的首节点,之前我在定义时去指定,会出现bug
			while (q) {//进行排序
				if (t->freq >= q->freq) {//当找到的节点的freq大于等于当前遍历节点时,插入
					t->next = q;
					q->pre = t;
					preQ->next = t;
					t->pre = preQ;
					break;
				}
				preQ = q;
				q = q->next;
			}
			break;
		}
		pre = p;
		p = p->next;
	}
	if (!flag) {
		printf("未找到该元素,序列不做改变");
	}
}
int main() {
	int n, data,num;
	struct Link *head = (struct Link *)malloc(sizeof(struct Link));
	head->next = NULL;
	head->pre = NULL;
	struct Link *p = head;
	printf("请输入节点个数:n=");
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		printf("请输入第%d个节点值:", i + 1);
		scanf("%d", &data);
		struct Link *newP = (struct Link*)malloc(sizeof(struct Link));
		newP->data = data;
		newP->pre = p;
		newP->freq = 0;
		p->next = newP;
		p = newP;
	}
	p->next = NULL;
	do {
		printf("请输入要查找的值,输入9999代表结束:num=");
		scanf("%d",&num);
		if (num == 9999)continue;//如果num=9999,跳入下一次循环
		locate(head,num);
		//p = head->next;
		printf("调整后链表为:\n");
		while (p) {
			printf("%d ",p->data);
			p = p->next;
		}
	} while (num!=9999);
	return 0;
}

寻找倒数第K个元素-2009

/*
	有一个带头结点的单链表,设计一个函数找到指定的倒数第k个节点,输出节点值,并返回1,否则返回0,前提不能改变链表,尽可能高效

	分析:
		我们可以先统计出总共的节点数count,那么该节点的顺序数num=count-k+1,当然如果k>count,直接返回0,时间复杂度为O(n)
		这里还有另一种更加便捷的方法,只需对链表遍历一次,我们设立两个指针,最开始均指向首节点,然后让q先移动k个节点,之后p
		q同步移动,当q为NULL时,p所在的位置便是倒数第k个节点的位置
*/
struct Link {
	int data;
	struct Link *next;
};
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int findTheReciprocalK(Link *h,int k) {//这是第一种解法
	struct Link *p = h->next;
	int count = 0,num;
	while (p) {//统计元素个数
		count++;
		p = p->next;
	}
	p = h->next;
	if (k > count) {
		return 0;
	}
	else {
		num = count - k + 1;
		while (--num) {//这里要用--num,如果用num--,会导致p为下一个元素,注意
			p = p->next;
		}
		printf("%d",p->data);
		return 1;
	}
}
int findTheReciprocalK2(Link *h,int k) {//这是第二种解法
	struct Link *p = h->next, *q = h->next;
	int count = k;
	while (count--) {
		if (q==NULL) {
			printf("倒数第%d个节点不存在",k);
			return 0;
		}
		q = q->next;
	}
	while (q!=NULL) {
		p = p->next;
		q = q->next;
	}
	printf("倒数第%d个节点值为:%d",k,p->data);
	return 1;
}
int main() {
	int k;
	struct Link *head;
	Link *createLink(int);
	head = createLink(0);
	printf("请输入要查倒数第几个数:k=");
	scanf("%d",&k);
	//findTheReciprocalK(head,k);
	findTheReciprocalK2(head, k);

	return 0;
}

寻找公共后缀-2012

/*
	存在这样一种情况,如果两个单词有相同的后缀,那我们可以将后缀作为公共部分存储,比如being和loading,其中ing就可以作为
	公共部分,现在存在两个链表,含有公共部分,设计一个高效算法找到其公共后缀其实位置。
	分析:
		我们可以这样想,如果我们单纯的让两条链表的指针同步移动,那么只有两条链表长度相同时才可能在公共部分的起始位置相遇,
		所以我们应该让他们处于同一起跑线上,故而我们应该让较长的链表先走,具体走多少,应该是走过两条链表的长度之差。

*/
struct Link {
	union 
	{
		int data;
		char letter;

	};
	Link *next;
};
#include <stdio.h>
#include <stdlib.h>
Link *findCommonSuffix(Link *h1,Link *h2) {
	struct Link *p = h1->next, *q = h2->next;
	int countP =0, countQ = 0,gap;
	while (p) {//遍历,获取链表长度
		countP++;
		p = p->next;
	}
	while (q) {
		countQ++;
		q = q->next;
	}
	if (countQ>countP) {//让p指针始终指向较长的那条链表
		p = h2->next;
		q = h1->next;
		gap = countQ - countP;
	}
	else {
		p = h1->next;
		q = h2->next;
		gap = countP - countQ;
	}
	while (gap--) p = p->next;//长链表指针先行移动gap位
	while (q != p && q != NULL) {//当两指针不同或不为NULL时继续向后移动
		q = q->next;
		p = p->next;
	}
	return p;
	
}
int main() {
	struct Link *h1,*h2,*com,*p1,*p2,*start;
	Link *createLink(int);
	char p[] = "letter";//数据类型,char
	h1 = createLink(1);
	h2 = createLink(1);
	com = createLink(1);//公共部分
	p1 = h1->next;
	p2 = h2->next;
	while (p1->next)p1 = p1->next;//到达链尾
	while (p2->next)p2 = p2->next;
	p1->next = com->next;//链接公共部分
	p2->next = com->next;
	p1 = h1->next;
	p2 = h2->next;
	while (p1) {
		printf("%c",p1->letter);
		p1 = p1->next;

	}
	printf("\n");
	while (p2) {
		printf("%c",p2->letter);
		p2 = p2->next;

	}
	printf("\n");
	start=findCommonSuffix(h1,h2);
	printf("%c",start->letter);
	return  0;
}

删除绝对值相等的元素-2015

/*
	用单链表保存m个整数,节点的结构为[data][next],且|data|<=n。现要求设计一个时间复杂度尽可能高效算法,
	对于链表中绝对值相等的节点,仅保留第一次出现的节点而删除其余绝对值相等的节点。
	分析:
		题目中提到时间复杂度尽可能高效,其本上就是暗示我们采用空间换时间的方式。因为数据是小于等于n的,我们可以开辟一块
		大小为n的数组,初始值为0,之后我们遍历链表,节点值既是我们寻找的下标,如果下标所在的数组值为0,则将值变为1,如果
		数组值已经为1,则说明在此之前我们遇见过绝对值相同的元素,故将此节点删除。
*/
struct Link {
	union {
		int data;
	}type;
	struct Link *next;
};
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
void deleteRepAbs(Link *h,int n) {
	int *arr = (int *)malloc(sizeof(int)*n);//这里记住大小是sizeof(int)*n,如果只写n是不对的
	for (int i = 0; i < n;i++) {//赋初值0
		*(arr + i) = 0;
	}
	struct Link *pre = h, *p = h->next, *r;
	while (p) {
		if (*(arr+abs(p->type.data))==0) {//首次访问,作上记录
			*(arr + abs(p->type.data)) = 1;
			pre = p;
			p = p->next;
		}
		else {
			r = p->next;
			pre->next = p->next;//删除
			free(p);//释放
			p = r;
		}
	}
}
int main() {
	struct Link *head,*p;
	int n = 100;//我们这里默许创建的链表里的节点值的绝对值<=100
	Link *createLink(int);
	head = createLink(0);//0代表我要创建的链表值类型为int
	deleteRepAbs(head,n);
	p = head->next;
	printf("去重后链表为:");
	while (p) {
		printf("%d ",p->type.data);
		p = p->next;
	}
	return 0;
}

寻找环的入口

/*
	设计一个算法判断一个链表是否有环
	分析:我们可以想象一下,在一个有环的赛道上,有两个人跑步,一个人跑得快,一个人跑得慢,试想,时间充足的情况下,跑得快
	的那个人是不是会再次遇到跑的慢的人呢?所以对于这道题,我们也可以通过快慢指针来处理,p指针一次移动两个节点,q指针一次移动
	一个节点,如果他们再次相遇了,说明链表有环,如果p指针为NULL了,说明无环。同时我们需要记录p、q各走的步数,用以确定
	环的入口点
*/
struct Link {
	union {
		int data;
	}type;
	struct Link *next;
};
#include <stdio.h>
Link *isLoop(Link *h) {
	struct Link *fast = h, *slow = h;
	while (slow&&fast&&fast->next) {
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast) {//再次相遇,说明有环
			break;
		}
	}
	if (slow==NULL||fast==NULL||fast->next==NULL) {
		return NULL;
	}
	fast = h;
	while (fast != slow) {
		fast = fast->next;
		slow = slow->next;
	}
	return fast;
}
int main() {
	struct Link *head,*l,*s;
	int count = 0;
	Link *createLink(int);
	head = createLink(0);
	l = head;
	while (l->next) {
		l = l->next;
	}
	l->next = head->next->next->next;//手动设置一个环
	s=isLoop(head);
	if (s) {
		printf("链表环起始节点值为:%d",s->type.data);
	}
	else {
		printf("该链表无环");
	}
	return 0;
}

就地穿插单链表节点-2019

/*
	设线性表L=(a1,a2,a3,...,an-2,an-1,an),采用带头结点的单链表保存,设计一个空间复杂度为O(1)的
	算法,得到L'=(a1,an,a2,an-1,a3,an-2...)
	分析:
		这道题还是有一点复杂的,不过我们慢慢分析它。我们可以这样来操作,我们设置两个指针slow和fast,其中fast每次走两步,
		slow每次走一步,当fast到达链尾时,slow刚好处于链表中间节点,之前我们学过链表逆置,接下来我们把slow后面的节点逆置,
		链表就变成了(a1,a2,a3,...,an,an-1,an-2,...),然后我们从中间开始遍历,依次将节点插入到前面节点后面,即可完成要求
*/
struct Link {
	union {
		int data;
	}type;
	struct Link *next;
};
#include <stdlib.h>
#include <stdio.h>
void crossTheLink(Link *h) {
	void reverse(Link *);
	struct Link *fast = h->next, *slow = h->next,*mid;
	mid = (struct Link*)malloc(sizeof(struct Link));
	while (fast->next&&fast->next->next) {//寻找中间节点
		fast = fast->next->next;
		slow = slow->next;
	}
	mid->next = slow->next;
	reverse(mid);//逆转mid后面的节点
	//slow->next = mid->next;
	fast = h->next;
	slow->next = NULL;
	slow = mid->next;
	while (slow) {//逐个进行插入
		mid = slow->next;
		slow->next = fast->next;
		fast->next = slow;
		slow = mid;
		fast = fast->next->next;//因为将slow插入了,所以之前的fast下一个是现在的下两个
	}
}
void reverse(Link *h) {//头插法逆置
	struct Link *p = h->next,*r;
	h->next = NULL;
	while (p) {
		r = p->next;
		p->next = h->next;
		h->next = p;
		p = r;
	}
	
}
int main() {
	struct Link *head,*p;
	Link *createLink(int);
	head = createLink(0);
	crossTheLink(head);
	printf("交叉后的链表为:");
	p = head->next;
	while (p) {
		printf("%d ", p->type.data);
		p = p->next;
	}
	return 0;
}
  • 3
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

落难Coder

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

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

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

打赏作者

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

抵扣说明:

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

余额充值