数据结构——循环链表

目录

循环链表

循环链表与单链表的区别和联系

循环链表的访问顺序

循环队列的常用操作

排序

合并

合并并保留序列

反转

判空

后续操作待补充

代码实现


循环链表与单链表的区别和联系

循环链表和单链表最大区别就是最后一个节点指向头节点。遍历时节点迭代指针 p!=头节点 L就行了。

循环链表也算是继承自单链表,起到模拟环的效果。解决单链表无法首尾相连的弊端。故而,在结构上与单链表无异。

循环链表的访问顺序

由于循环链表起到模拟环的作用,所有访问顺序是一个圈,即遍历一次后能够返回到头节点。

循环队列的常用操作

除了增加、删除、查询、修改外,这里再补充几个操作。其实放在前面的单双队列也是有这些操作的,只是我没有写罢了。

常用操作
判空 清空链表保留头节点 合并多个链表
排序 合并后保持序列 反转
     

 

暂时只能想到这么多,往后我再补充。

排序

排序实际上在插入的过程中就可以完成。没插入一个节点,先找到这个节点应该放在哪个位置。这种不难。

难的是链表赋值之后再排序。一种最常见的方法就是冒泡,这种时间复杂度比较高,达到o(n2),有没有更快速的方法?

有的!o(nlogn)够不够,这种做法是

跑一遍链表,把链表里面的数值取出来放入数组,然后再跑一边快排,然后再放回链表就行了。于是我又想到第一篇文章里面的单链表快排能不能做到?如果按照这个思路来,是可以的!

了解各种排序时间复杂度和空间复杂度戳这里

 这里举快速排序的例子吧,算法时间复杂度高的咱不写。

用到了malloc.h中的

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))

 

  • base -- 指向要排序的数组的第一个元素的指针。
  • nitems -- 由 base 指向的数组中元素的个数。
  • size -- 数组中每个元素的大小,以字节为单位。
  • compar -- 用来比较两个元素的函数。

 使用这个函数之前需要知道节点数

int Count(Linklist L){
	Linklist p = L->next;
	int i = 0;
	while(p->next != L){
		i++;
		p = p->next;
	}
	
	return i+1;
}

 还要有一个比较函数(这个函数如果是 “-” 表示升序 “+”表示降序

int cmpfunc (const void * a, const void * b)
{
   return ( *(int*)a - *(int*)b );
}

 排序函数

Linklist sort(Linklist &L){
	int count = Count(L);
	int a[count],i=0;
	Linklist p = L->next;
	while(p->next != L){
		a[i] = p->value;
		p = p->next;
		i++;
	}
	a[i] = p->value;
	qsort(a,count,sizeof(int),cmpfunc);
	p = L->next;
	i = 0;
	while(p->next != L){
		p->value = a[i];
		i ++;
		p = p->next;
	}
	p ->value = a[i];
	return L;
} 

合并

有两种合并方式:

  1. 创建一个新的链表,然后把所需要合并的链表按次序赋值给新的链表。
  2. 遍历每个链表找到对应的尾节点,然后将后者的头节点与前者的尾节点相接

两种方式的优缺点分析:

方法一保留了原来的链表,但是创建了一个新链表,空间复杂度有所提高。

方法二破坏了原来的链表,使得所有链表合并成一个链表,空间上并没有多大改变,甚至还可以释放几个链表的头节点。

//function18 王道考研数据结构 p39 T18,循环链表合并
Linklist combine(Linklist &A,Linklist &B){
	Linklist p = A->next,q = B->next;
	while(p->next != A){
		p = p->next;	
	}
	while(q->next != B){
		q = q->next;
	}
	p->next = B->next;
	q->next = A;
	return A;
} 

 

合并并保留序列

创建一个新链表,然后把每个链表的数据都取出来,跑一遍排序,最后放入新链表。

反转

反转也有两种方法:

  1. 记录一下头节点,记录一下尾节点,跑一遍循环链表,使头节点尾节点位置互换。时间复杂度o(n2)
  2. 用数组把数据取出来,然后逆序放入链表。时间复杂度o(nlogn)

这里挑第二种方法跑了一遍代码、

Linklist reverse(Linklist &L){
	printf("revesing\n");
	Linklist p = L->next;
	int i = 0,count = Count(L);
	int a[count];
	while(p->next!= L){
		a[i] = p->value;
		p = p->next;
		i++;
	}
	a[i] = p->value;
	p = L->next;
	while(p!=L){
		p->value = a[i];
		p = p->next;
		i--;
	}
	return L;
}

 

判空

对于循环链表来说,判空等价于头节点的next是否指向自己或者链表记录数据的节点数为0。

单链表就是记录数据的节点数为0或者头节点的next指向空

后续操作待补充

代码实现

代码只有一些常规操作,新增的一些操作会在后期补上。

#include
#include

typedef struct CNode{
	int value;
	struct CNode *next;
}Cnode,*Linklist;

Linklist initList(Linklist &L){
	L = (Linklist)malloc(sizeof(Cnode));
	L->next = L;
	return L;
}
//一个一个插入数据
Linklist insert(Linklist &L){
	int x;
	printf("input x\n");
	scanf("%d",&x);
	Linklist p = (Linklist)malloc(sizeof(Cnode)),q = L;
	p->value = x;
	p->next = L;
	while(q->next != L){
		q = q->next;
	}
	q->next = p;
}

void display(Linklist L){
	Linklist p = L->next;
	int i = 0 ;
	while(p != L){
		printf("%d ",p->value);
		p = p->next;
		i ++;
	}
	printf("\n");
	return;
}

int main(){
	Linklist L,A,B;
	initList(A);
	initList(B);
	int i = 0;
	while(i<4){
		insert(A);
		insert(B);
		i ++;
		display(A);
		display(B);
	}
	//combine(A,B);
	//display(A);
	//Emptyandfree(B);
	display(B);
}

 

你可能感兴趣的:(C++数据结构)