链表及跳表详解

链表

链表基础

首先链表有数据域和指针域,其次链表在内存中不是连续分布的,链表查找慢,增删灵活。

链表有以下几种类型:

(1)单向链表: 头增头删快,因为一般都是给链表表头没有表尾的概念了。

(2)双向链表: 每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点,既可以向前查询也可以向后查询。

(3)循环链表:链表首尾相连。

链表的创建

我们用尾添加来创建一个链表,如果只有一个表头变量Head,那么我们每添加一个节点都要遍历一遍当前链表,有些慢,所以我们引入一个表尾变量Tail来辅助创建链表。

步骤:

(1)Head(是创建完给出的)Tail(是辅助创建用的,不是应该得到的)

(2)结构体

typedef struct node
{
	int value;
	struct node* pNext;
}List;

       (注:typedef struct node在C语言中要使用,不然定义一个指针都要像这样:struct node *p;)

(3)创建

         首先将当前数据装到对应节点中num->node。

         其次将当前节点放到链表中node->list。

代码:

#include<stdio.h>
typedef struct node
{
	int value;
	struct node* pNext;
}List;
List* Creat() {
	List* pHead=NULL;
	List* pTail=NULL;
	List* pTemp = NULL;
	printf_s("输入链表长度\n");
	int len;
	scanf_s("%d", &len);
	while (len--)
	{
		int num;
		scanf_s("%d", &num);
		pTemp = (List*)malloc(sizeof(List));
		pTemp->value = num;
		//初值必须放,要不然会变成野地址
		pTemp->pNext = NULL;
		if (pHead == NULL) {
			pHead = pTemp;
		}
		else {
			pTail->pNext = pTemp;
		}
			pTail = pTemp;
	}  
	return pHead;
}
void Print(List* pH)
{
	while(pH)
	{
		printf_s("%d ", pH->value);
		pH = pH->pNext;
	}
}
int main() {
	List* pHead = NULL;
	pHead = Creat();
	Print(pHead);
	return 0;
}

单向链表的倒序输出

采用递归,设置一个明确的终点,一次次往下遍历,如果最后为NULL,就是遍历到头了。

#include<stdio.h>
typedef struct node {
	int value;
	struct node* pNext;
}List;
List* Create()
{
	int len;
	int num;
	List* pHead = NULL;
	List* pTail = NULL;
	List* pTemp = NULL;
	printf_s("输入链表长度\n");
	scanf_s("%d", &len);
	while (len--)
	{
		pTemp = (List*)malloc(sizeof(List));
		scanf_s("%d", &num);
		pTemp->value = num;
		pTemp->pNext = NULL;
		if (pHead == NULL) 
			pHead = pTemp;
		else 
			pTail->pNext = pTemp;
		pTail = pTemp;
	}
	return pHead;
}
//递归法
List* Reprint(List* pHead)
{
	//如果为空那就遍历到头了
	if (pHead == NULL) return;
	Reprint(pHead->pNext);
	printf_s("%d ", pHead->value);
}
int main()
{
	List* pHead = NULL;
	pHead=Create();
	Reprint(pHead);
	return 0;
}

链表倒置

A->B->C->D变为D->C->B-A

使用头插法:

首先我们设置一个新的头节点,NewHead指向新的头节点,然后我们把A B之间断开,把A带走,A是带走的节点Take指向A,B是被断开的节点Broke指向B。

然后把下面三个指针平移,继续做同样操作

到最后我们只要把,C D连接上就行,而且当Broke指向NULL时,倒置完毕

步骤:
(1)没有或者一个节点

(2)NewHead Take Broke 创建及指向

(3)改变指向,标记位置迭代

#include<stdio.h>
typedef struct node
{
	int value;
	struct node* pNext;
}List;
List* Create()
{
	List* pHead = NULL;
	List* pTail = NULL;
	List* pTemp = NULL;
	int len;
	scanf_s("%d", &len);
	while (len--)
	{
		int num;
		scanf_s("%d", &num);
		pTemp = (List*)malloc(sizeof(List));
		pTemp->pNext = NULL;
		pTemp->value = num;
		if (pHead == NULL)
			pHead = pTemp;
		else pTail->pNext = pTemp;
		pTail = pTemp;
	}
	return pHead;
}
List* reList(List* pHead)
{
	//若为空,或者只有一个数字
	if (pHead == NULL || pHead->pNext == NULL) return pHead;
	List* newHead = NULL;
	List* pTake = pHead;
	List* pBroke = pHead->pNext;
	//反转
	while (1)
	{
		//改变方向
		pTake->pNext = newHead;
		if (pBroke == NULL) return pTake;
		newHead = pTake;
		pTake = pBroke;
		pBroke = pBroke->pNext;
	}
}
void Print(List* pHead)
{
	while (pHead)
	{
		printf("%d ", pHead->value);
		pHead = pHead->pNext;
	}
}
int main()
{
	List* pHead = NULL;
	pHead = Create();
	pHead = reList(pHead);
	Print(pHead);
	return 0;
}

两链表合并

两条有序链表合并成一条链表

1->3->6

2->4->7->8

先两个表表头比较 ,1<2,1成为新的表头,1现在是tail也是head,然后1的下一个3和2比较,让1指向2,表尾更新为2,然后让2的下一个4和3比较,表尾更新为3,然后往后继续比较,直到其中一个链表空了,如果有一个链表不为空,把剩下的添加到tail后面。

步骤:

(1)先确定新链表表头

(2)循环比较

(3)将有剩余部分连接

#include<stdio.h>
typedef struct node
{
	int value;
	struct node* pNext;
}List;
List* Create()
{
	List* pHead = NULL;
	List* pTail = NULL;
	List* pTemp = NULL;
	int len;
	printf_s("输入链表长度\n");
	scanf_s("%d", &len);
	while (len--)
	{
		int num;
		scanf_s("%d", &num);
		pTemp = (List*)malloc(sizeof(List));
		pTemp->pNext = NULL;
		pTemp->value = num;
		if (pHead == NULL)
			pHead = pTemp;
		else pTail->pNext = pTemp;
		pTail = pTemp;
	}
	return pHead;
}
List* Merage(List* pHead1, List* pHead2)
{
	if (pHead1 == NULL) return pHead2;
	if (pHead2 == NULL) return pHead1;
	List* pnewHead = NULL;
	List* pTail = NULL;
	//先找头节点
	pnewHead = ((pHead1->value) < (pHead2->value)) ? pHead1 : pHead2;
	pTail = pnewHead;
	if (pnewHead == pHead1) pHead1 = pHead1->pNext;
	else pHead2 = pHead2->pNext;
	//循环比较
	while (pHead1!=NULL && pHead2!=NULL)
	{
		pTail->pNext = ((pHead1->value) < (pHead2->value)) ? pHead1 : pHead2;
		pTail = pTail->pNext;
		if (pTail == pHead1) pHead1 = pHead1->pNext;
		else pHead2 = pHead2->pNext;
	}
	//检测是否有剩余
	if (pHead1 != NULL)pTail->pNext = pHead1;
	else pTail->pNext = pHead2;
	return pnewHead;
}
void Print(List* pHead)
{
	while (pHead)
	{
		printf("%d ", pHead->value);
		pHead = pHead->pNext;
	}
}
int main()
{
	List* pHead1 = NULL;
	List* pHead2 = NULL;
	pHead1 = Create();
	pHead2 = Create();
	List* pHead = NULL;
	pHead = Merage(pHead1, pHead2);
	Print(pHead);
	return 0;
}

跳表(SkipList)

跳表的前提是为有序链表服务的,是redias底层五种数据结构之一。

跳表的查询增删时间复杂度O(logn)。

我们这里用有5个节点的链表举例,我们现在做查询操作,类似于二分。

因为我们有5个节点,要3层(log5),随机决定长不长上一层(如果像二分一样均分,增删是致命的,要删了某一个整个表都得动),构成类似于简单网状结构。

花费空间:O(n)     n/2+n/4+n/8.......

如果查询13,那么L2层它的范围就是哨兵->18,L1层12->18,由于没有13所以查找失败。

如果插入15,那就找15的前一个节点,L2层它的前一个节点是哨兵,L1层它的前一个节点是12,L0层它的前一个节点是12。

------------------------------明天还是会更新其余链表类型题和相应知识点及思路------------------------------

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值