数据结构之List(双向链表)的实现

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

链表要实现的对外方法

方法名 参数 功能 返回
find const T & val, int n, listNode * p 区间查找 从p往前数n个节点 指针或NULL
const T & val, listNode * p 区间查找 从p往前到首节点
const T & val 查找
Size void 链表规模 size
empty void 判空 bool
first void 返回首节点 首节点指针
clear void 清空链表 void
insertAsFirst const T & val 作为首节点插入 新节点指针
insertAsLast const T & val 作为末节点插入
insertBefore const T & val, listNode * node 在某节点前插入
insertAfter const T & val, listNode * node 在某节点后插入
remove listNode * node 移除 后一个节点的指针
unique void 无序链表唯一化 删多少个
traverse T2 & visit 遍历 void
unique_ordered void 有序链表唯一化 删多少个
unique void 无序链表唯一化 删多少个
unique void 无序链表唯一化 删多少个
selectionSort void 选择排序,范围全部 void
listNode* p, int n 范围从p往前数n个节点 void
insertionSort void 插入排序,范围全部 void
listNode* p, int n 范围从p往前数n个节点 void

总结

  1. 函数的分割。插入操作就很方便。

  2. 在按指针遍历的循环中for (listNode<T> * p = head; p->succ != tail; p = p->succ,使用List<T>::remove(),是危险的。执行完remove(p)后,p将成为野指针。p=p->succ将发生错误。而Vector<T>::remove()是安全的。若非要执行remove(p),请改成p = p->succ;remove(p->pred); 本程序中也可写为p = remove(p->pred);

  3. listNode为什么定义成struct而非class,因为List要经常对每个节点的前驱与后继做修改,所以pred、succ、data都是public的,所以listNode定义为struct

  4. 有序容器和无序容器的查找的区别在于,无序find()找不到返回NULL,有序search()可以返回失败位置。

code 链节

链表由节点连成,节点的定义如下文章来源地址https://www.toymoban.com/news/detail-598540.html

# pragma once
# include <iostream>

template <typename T>
struct listNode {
	T data;
	listNode <T>* pred;
	listNode <T>* succ;
	
	listNode(){}
	listNode(T d, listNode<T>* p, listNode<T>* s):data(d), pred(p), succ(s){}

	T getData(void) { return data; }
	listNode<T> getPred(void) { return pred; }
	listNode<T> getSucc(void) { return succ; }

	listNode <T>* insertAsPred(const T & e)
	{
		listNode <T> * tmp = new listNode <T>(e, pred, this);
		pred->succ = tmp;
		pred = tmp;
		return tmp;
	}

	listNode <T>* insertAsSucc(const T & e)
	{
		listNode <T> * tmp = new listNode <T>(e, this, succ);
		succ->pred = tmp;
		succ = tmp;
		return tmp;
	}
};

code 链表

# pragma once
# include "listNode.h"

template <typename T>
struct printList {
	void operator () (T* pnode)
	{
		std::cout << pnode->data << std::endl;
	}
};

template <typename T>
class List {
public:

	// 构造函数
	List() { init();}

	// 析构函数
	~List()
	{
		for (listNode<T> * currentNode = head->succ; currentNode; currentNode = currentNode->succ)
		{
			delete currentNode->pred;
		}
		delete tail;
	}

	//***********************************************************只读*********************************************************

	// 区间查找 从p往前数n个节点的范围
	listNode<T> * find(const T & val, int n, listNode<T> * p) const
	{
		while (n-- && p != head && p->data != val)
		{
			p = p->pred;
		}
		if (n == 0 && p == head) return NULL;
		else return p;
	}

	// 区间查找 从p往前到首节点的范围
	listNode<T> * find(const T & val, listNode<T> * p) const
	{
		while (p != head && p->data != val)
		{
			p = p->pred;
		}
		if (p == head) return NULL;
		else return p;
	}

	// 全体查找
	listNode<T> * find(const T & val) const
	{
		listNode<T> * find(val, tail->pred);
	}

	//***********************************************************可写*********************************************************

	// 清空
	void clear()
	{
		for (listNode<T> * currentNode = head->succ->succ; currentNode; currentNode = currentNode->succ)
		{
			delete currentNode->pred;
		}
		size = 0;
		head->pred = NULL;
		head->succ = tail;
		tail->pred = head;
		tail->succ = NULL;
	}

	// 作为首节点插入
	listNode<T> * insertAsFirst(const T & val) { ++size;  return head->insertAsSucc(val); }
	// 作为末节点插入
	listNode<T> * insertAsLast(const T & val) { ++size;  return tail->insertAsPred(val); }
	// 在某节点前插入
	listNode<T> * insertBefore(const T & val, listNode<T> * node) { ++size;  return node->insertAsPred(val); }
	// 在某节点后插入
	listNode<T> * insertAfter(const T & val, listNode<T> * node) { ++size;  return node->insertAsSucc(val); }

	// 移除
	listNode<T> * remove(listNode<T> * node)
	{
		if (node == NULL) return NULL;
		--size;
		listNode<T> * succNode = node->pred->succ = node->succ;
		node->succ->pred = node->pred;
		delete node;
		return succNode;
	}

	// 唯一化
	int unique(void)
	{
		int oldSize = size;
		for (listNode<T> * p = head->succ; p != tail; p = p->succ)
		{
			remove(find(p->data, p->pred));
		}
		return oldSize - size;
	}

	// 基于复制的构造

	//***********************************************************遍历*********************************************************
	template <typename T2> void traverse(T2 & visit)
	{
		for (listNode<T> * p = head->succ; p != tail; p = p->succ)
		{
			visit(p);
		}
	}

	//*****************************************************针对有序链表的操作***************************************************

	// 有序链表唯一化
	int unique_ordered(void)
	{
		// 如果该节点与前驱data一样,删掉前驱
		if (size < 2) return 0;
		int oldSize = size;
		for (listNode<T> *p = head->succ->succ; p != tail; p = p->succ)
		{
			if (p->data == p->pred->data)
			{
				remove(p->pred);
			}
		}
		return oldSize - size;
	}

	// 查找
	listNode<T> * search(const T & data, listNode<T> * p, int n)
	{
		while (n-- && p != head && data < p->data)
		{
			p = p->pred;
		}
		return p;
	}

	//***********************************************************排序*********************************************************
	
	// 插入排序
	void insertionSort(listNode<T>*p, int n);
	void insertionSort(void){ insertionSort(head->succ, size);}

	// 选择排序
	void selectionSort(listNode<T>*p, int n);
	void selectionSort(void) { selectionSort(head->succ, size);}

protected:

	// 初始化双向链表
	void init(void)
	{
		size = 0;
		head = new listNode<T>;
		tail = new listNode<T>;
		head->pred = NULL;
		head->succ = tail;
		tail->pred = head;
		tail->succ = NULL;
	}

	// 从p开始往前找n个,这些data之中最大的,且最右的
	listNode<T>* Max(listNode<T>* p, int n);

private:
	listNode<T> * head;
	listNode<T> * tail;
	int size;

};

// 选择排序
template <typename T>
void List<T>::selectionSort(listNode<T>*p, int n)
{
	// 找到有序后缀(注意,初始长度为0)
	listNode<T>* pos = p; //一会儿要在pos之前插入
	for (int i = 1; i <= n; ++i) pos = pos->succ;

	for (int rank = 0; rank < n; ++rank, pos = pos->pred) //有序后缀的长度
	{
		// 找前缀中最大值
		listNode<T> * tmp_max = Max(pos->pred, n - rank);

		// 插入后缀最前面
		insertBefore(tmp_max->data, pos);
		remove(tmp_max);
	}
}

// 从p开始往前找n个,这些data之中最大的,且最右的
template <typename T>
listNode<T>* List<T>::Max(listNode<T>* p, int n)
{
	listNode<T> * tmp_max = p;
	while (n-- && p != head)
	{
		if (p->data > tmp_max->data) tmp_max = p;
		p = p->pred;
	}
	return tmp_max;
}

// 插入排序
template <typename T>
void List<T>::insertionSort(listNode<T>*p, int n)
{
	for (int rank = 1; rank <= n; ++rank) //有序前缀的长度
	{
		listNode<T>* pos = search(p->data, p->pred, rank);
		insertAfter(p->data, pos);
		//p = p->succ;
		p = remove(p->pred);
	}
}

到了这里,关于数据结构之List(双向链表)的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 数据结构与算法:双向链表

    朋友们大家好啊,在上节完成单链表的讲解后,我们本篇文章来对 带头循环双向链表进行讲解 单链表中,一个节点存储数据和指向下一个节点的指针,而双向链表除了上述两个内容,还包括了 指向上一个节点的指针 带头的双向链表,是指在双向链表的最前端添加了一个 额

    2024年02月20日
    浏览(50)
  • 【数据结构与算法】双向链表

    作者:旧梦拾遗186 专栏:数据结构成长日记   带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。 现在我们来通

    2024年02月11日
    浏览(57)
  • 数据结构与算法(四):双向链表

    双向链表概念和单向链表是一致的,区别在于双向链表在单向链表的基础上,指针区域多了一个指向上一个节点的指针。单向链表内容可以参考我的上一篇文章:http://t.csdn.cn/Iu56H。 基本的数据结构如图所示: 双向链表结构包含了节点的数据内容和两个指针:指向前一个节点

    2024年02月14日
    浏览(51)
  • 算法竞赛基础:C++双向链表的结构和实现(普通链表、List、静态链表)

    本文将会介绍在算法竞赛中双向链表的几种使用方式,适合有一定基础的人阅读。 一般来说,普通的链表结构是这样的: next指针指向下一个链表,这样的结构只能够支持单向查询。 双向链表,顾名思义,就是可以支持双向的访问和查询。 也就是这样的: 这种链表为访问前

    2024年01月24日
    浏览(41)
  • 数据结构——实现双向链表

    怎么说呢?光乍一听名字好像很难的样子是吧,那如果你这样认为的话,可就要让你大跌眼镜了哦,其实双向带头循环链表从操作和理解上来说都是要易于单项不带头不循环链表(俗称单链表)的。 咱们就来见识见识吧!希望真的能让你们“大跌眼镜”哈! 双向带头循环链

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

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

    2024年03月26日
    浏览(65)
  • 【(数据结构)— 双向链表的实现】

    注意: 这里的 “带头” 跟前面我们说的 “头节点” 是两个概念,实际前面的在单链表阶段称呼不严 谨,但是为了同学们更好的理解就直接称为单链表的头节点。 带头链表里的头节点,实际为 “哨兵位” ,哨兵位节点不存储任何有效元素,只是站在这里“放哨 的” “哨

    2024年02月06日
    浏览(46)
  • 【数据结构】双向链表的实现

    我要扼住命运的咽喉,他却不能使我完全屈服。                      --贝多芬 目录 一.带头循环的双向链表的特点 二.不带头不循环单向链表和带头循环的双向链表的对比 三.初始化链表,创建哨兵结点 四.双向链表的各种功能的实现 1.双向链表的尾插 2.双向链表的打印 

    2023年04月10日
    浏览(46)
  • 数据结构——双向链表的实现

    注意: 双向链表又称带头双向循环链表 这⾥的“带头”跟前⾯我们说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严 谨,但是为了同学们更好的理解就直接称为单链表的头节点。 带头链表⾥的头节点,实际为“ 哨兵位 ”,哨兵位节点不存储任何有效元素,只

    2024年02月06日
    浏览(47)
  • 数据结构与算法 | 链表(Linked List)

    链表(Linked List)是一种线性数据结构,它由一系列节点(Node)组成,每个节点包含两部分:数据和指向下(上)一个节点的引用(或指针)。链表中的节点按照线性顺序连接在一起(相邻节点不需要存储在连续内存位置),不像数组一样存储在连续的内存位置。链表通常由

    2024年02月08日
    浏览(50)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包