链表及跳表详解

这篇具有很好参考价值的文章主要介绍了链表及跳表详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

链表

链表基础

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

链表有以下几种类型:

(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++

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

链表及跳表详解,链表,数据结构,算法,c++

链表及跳表详解,链表,数据结构,算法,c++

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

链表及跳表详解,链表,数据结构,算法,c++

步骤:
(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),随机决定长不长上一层(如果像二分一样均分,增删是致命的,要删了某一个整个表都得动),构成类似于简单网状结构。链表及跳表详解,链表,数据结构,算法,c++

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

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

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

------------------------------明天还是会更新其余链表类型题和相应知识点及思路------------------------------文章来源地址https://www.toymoban.com/news/detail-831684.html

到了这里,关于链表及跳表详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】详解链表结构

    上篇博客已经介绍了顺序表的实现:【数据结构】详解顺序表。最后在里面也谈及了顺序表结构的缺陷,即 效率低,空间浪费 等等问题,那么为了解决这些问题,于是乎我们引入了链表的概念,下面将对链表结构进行讲解 首先肯定会问,到底什么是链表? 链表的概念 : 链

    2024年02月05日
    浏览(44)
  • 【数据结构】3.跳表和散列

    跳表可以近似实现二分查找的效果 以下面长度为7的链表举例,该跳表通过3条链表进行存储。假设要查找元素80: 首先在第2级链表查找,因为80大于40,所以在第3个节点右侧查找 然后在第1级链表查找,因为80大于75,所以在第5个节点右侧查找 最后在第0级链表查找,找到元素

    2024年02月08日
    浏览(45)
  • 【数据结构】链表详解

    ☃️个人主页:fighting小泽 🌸作者简介:目前正在学习C语言和数据结构 🌼博客专栏:数据结构 🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻 在前面我们已经学习过了有关顺序表的知识,但是我们知道顺序表是存在着一些问题的 问题: 中间/头部的插入删除,时间复杂度

    2024年02月03日
    浏览(41)
  • [数据结构]——链表详解

    1.什么是链表?链表的概念及结构 链表是一种 物理存储结构上非连续、非顺序 的存储结构,数据元素的 逻辑顺序 是通过链表中的 指针链接 次序实现的。 注意: 1.链式结构在逻辑上是连续的,但在物理上不一定连续 2.现实中的结点一般都是从堆上申请出来的 3.从堆上申请的

    2024年02月09日
    浏览(37)
  • 数据结构——链表详解

    new一个奶黄包:没关系,这条路我陪你走到底 单链表结构图 带头单循环链表结构图 双向循环链表结构图 带头双向循环链表结构图 链表特点 单链表在内存中,并不是连续存储的(逻辑上连续)。 不支持随机访问 插入时只需要改变指针指向 没有容量的概念 可以高效的在任意

    2024年02月12日
    浏览(37)
  • 【数据结构】链表 详解

    我们不废话,直入正题。 什么是链表? 来看看百度怎么说: 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包

    2023年04月19日
    浏览(42)
  • 数据结构入门 — 链表详解_双向链表

    数据结构入门 — 双向链表详解 博客主页链接:https://blog.csdn.net/m0_74014525 关注博主,后期持续更新系列文章 文章末尾有源码 *****感谢观看,希望对你有所帮助***** 第一篇:数据结构入门 — 链表详解_单链表 第二篇:数据结构入门 — 链表详解_双向链表 第三篇:数据结构入门

    2024年02月11日
    浏览(48)
  • 数据结构:详解【链表】的实现(单向链表+双向链表)

    1.顺序表的问题和思考 问题: 中间/头部的插入删除,时间复杂度为O(N)。 增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据

    2024年03月26日
    浏览(65)
  • 数据结构:跳表的原理和运用

    本篇总结的是跳表的相关内容 skiplist 本质上也是一种查找结构,用于解决算法中的查找问题,跟平衡搜索树和哈希表的价值是一样的,可以作为 key 或者 key/value 的查找模型 假如我们每相邻两个节点升高一层,增加一个指针,让指针指向下下个节点,如下图所示。这样所有新

    2024年02月22日
    浏览(48)
  • 10.Redis数据结构之跳表

    sortedSet是Redis提供的一个非常特别的数据结构,常用作排行榜等功能,将用户id作为value,关注时间或者分数作为score进行排序。 与其他数据结构相似,zset也有两种不同的实现,分别是zipList和(hash+skipList)。 规则如下: zipList满足以下两个条件 [score,value]键值对数量少于128个;

    2024年02月11日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包