《数据结构算法与应用 —— C++语言描述》学习笔记 — 字典 — 链表实现

Posted coding-hwz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《数据结构算法与应用 —— C++语言描述》学习笔记 — 字典 — 链表实现相关的知识,希望对你有一定的参考价值。

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 字典 — 链表实现

虽然在n个元素的有序数组上二分查找所需要的时间为 O ( l o g n ) O(logn) O(logn),但是有序链表上查找所需要的时间为 O ( n ) O(n) O(n)。为了提高有序链表的查找性能,可以在全部或部分节点上增加额外的指针。在查找时,通过这些指针,可以跳过链表的若干个节点,不必从左到右连续查看所有节点。

增加了额外的向前指针的链表叫做跳表。它采用随机技术来决定链表的哪些节点应增加向前指针,以及增加多少个指针。基于这种随机技术,跳表的查找、插入、删除的平均时间复杂度为 O ( l o g n ) O(logn) O(logn)。然而,最坏情况下的时间复杂度却变成 O ( n ) O(n) O(n)

散列是用来查找、插入、删除的另一种随机方法。与跳表相比,它把操作时间提高到 O ( 1 ) O(1) O(1),但最坏情况下的时间复杂度仍为 O ( n ) O(n) O(n)。尽管如此,如果经常需要按序输出所有元素或按序查找元素,那么跳表的执行效率将优于散列。

一、字典

字典是由一些形如 (k,v) 的数对所组成的集合,其中 k 是关键字,v 是与关键字 k 对应的值。任意两个数对,其关键字都不等。

多重字典与字典类似,只是两个或更多的数对可以有相同的关键字。在韦氏字典中,一个词可能有多个词条,对应于不同的意义、发音等。这样的形式就是多重字典。

在一个多重字典中进行查找和删除操作时,需要消除歧义。就查找而言,有两种可能:
(1)查找任何一个具备给定关键字的数对;
(2)查找所有具备给定关键字的数对。
就删除操作而言,我们要求用户提供与给定关键字对应的所有数对,由用户选择删除哪些数对。此外,可以任意删除与给定关键字对应的一个数或所有数对。

查找操作可以按照给定的关键字,随机地存取字典中的数对。有些字典的应用还有另一种存取模式 — 顺序存取。在这种模式中,利用迭代器,按关键字的升值顺序逐个查找字典数对。本章设计的所有字典是现代吗既可以随机存取,也可以顺序存取。

二、抽象数据类型

/*************************************************
Author: coding-hwz
Date:2021-08-24
Description: 字典抽象类,用于提供字典接口操作
**************************************************/

#pragma once
#include <utility>

template<typename Key, class Value>
class dictionary
{
public:
	virtual ~dictionary() {};

	/*
	 * @brief 判断字典是否为空
	 * @return true  空
	 * @return false 非空
	 */
	virtual bool empty() const = 0;

	/*
	 * @brief 返回字典数据长度
	 * @return 字典数据长度
	 */
	virtual int size() const = 0;

	/*
	 * @brief 查找给定key对应的值
	 * @param key 给定键
	 * @return 键值对对象
	 */
	virtual std::pair<Key, Value>* find(const Key& key) const = 0;

	/*
	 * @brief 删除某键对应的键值对
	 * @param key 给定键
	 */
	virtual void erase(const Key& key) = 0;

	/*
	 * @brief 向字典中插入新的键值对
	 * @param keyValue 键值对对象
	 */
	virtual void insert(const std::pair<Key, Value>& element) = 0;
};

三、链表描述

1、节点

/*************************************************
Author: coding-hwz
Date:2021-08-24
Description: 链表字典类,节点声明
**************************************************/

#pragma once
#include <utility>

template<typename Key, typename Value>
struct pairNode
{
	std::pair<Key, Value> element;
	pairNode* next;
};

2、接口声明

/*************************************************
Author: coding-hwz
Date:2021-08-24
Description: 链表字典类
**************************************************/

#pragma once
#include "dictionary.h"
#include "pairNode.h"

template<typename Key, typename Value>
class sortedChain : public dictionary<Key, Value>
{
public:
	typedef struct pairNode<Key, Value> node_type;
	using value_type = typename std::pair<Key, Value>;

	// copy control
	sortedChain() = default;
	sortedChain(const sortedChain& other);
	sortedChain(sortedChain&& other);
	virtual ~sortedChain() override;
	sortedChain& operator=(const sortedChain& other);
	sortedChain& operator=(sortedChain&& other);

	virtual bool empty() const override;
	virtual int size() const override;
	virtual value_type* find(const Key& key) const override;
	virtual void erase(const Key& key) override;
	virtual void insert(const value_type& element) override;

private:
	node_type* firstNode = nullptr;
	std::size_t dictionarySize = 0;

	/*
	 * @brief 交换两个字典的数据
	 * @param other 另一个字典对象
	 */
	void swap(sortedChain& other);

	/*
	 * @brief 拷贝另一个字典的数据
	 * @param other 另一个字典对象
	 * @return 拷贝生成的临时对象
	 */
	sortedChain makeCopy(const sortedChain& other);

	void makeCopyAndSwap(const sortedChain& other);
};

3、拷贝控制接口

template<typename Key, typename Value>
sortedChain<Key, Value>::sortedChain(const sortedChain& other)
{
	makeCopyAndSwap(other);
}

template<typename Key, typename Value>
sortedChain<Key, Value>::sortedChain(sortedChain&& other)
{
	swap(other);
}

template<typename Key, typename Value>
sortedChain<Key, Value>::~sortedChain()
{
	while (firstNode != nullptr)
	{
		auto nextNode = firstNode->next;
		delete firstNode;
		firstNode = nextNode;
	}
}

template<typename Key, typename Value>
sortedChain<Key, Value>& sortedChain<Key, Value>::operator=(const sortedChain& other)
{
	makeCopyAndSwap(other);
	return *this;
}

template<typename Key, typename Value>
sortedChain<Key, Value>& sortedChain<Key, Value>::operator=(sortedChain&& other)
{
	swap(other);
	return *this;
}

template<typename Key, typename Value>
void sortedChain<Key, Value>::swap(sortedChain& other)
{
	using std::swap;
	swap(dictionarySize, other.dictionarySize);
	swap(firstNode, other.firstNode);
}

template<typename Key, typename Value>
sortedChain<Key, Value> sortedChain<Key, Value>::makeCopy(const sortedChain& other)
{
	sortedChain returnChain;
	if (other.firstNode == nullptr)
	{
		return returnChain;
	}

	returnChain.firstNode = new node_type(*other.firstNode);
	auto currentNodeThis = returnChain.firstNode;
	auto currentNodeOther = other.firstNode;
	while (currentNodeOther->next != nullptr)
	{
		currentNodeThis->next = new node_type(*currentNodeOther->next);
		currentNodeThis = currentNodeThis->next;
		currentNodeOther = currentNodeOther->next;
	}

	dictionarySize = other.dictionarySize;
	return returnChain;
}

template<typename Key, typename Value>
inline void sortedChain<Key, Value>::makeCopyAndSwap(const sortedChain& other)
{
	auto copySortedChain = makeCopy(other);
	swap(copySortedChain);
}

这里我们主要是通过 makeCopyswap 接口实现的拷贝和移动功能

4、容量接口

template<typename Key, typename Value>
bool sortedChain<Key, Value>::empty() const
{
	return dictionarySize == 0;
}

template<typename Key, typename Value>
int sortedChain<Key, Value>::size() const
{
	return dictionarySize;
}

5、修改接口

template<typename Key, typename Value>
auto sortedChain<Key, Value>::find(const Key& key) const -> sortedChain<Key, Value>::value_type*
{
	auto currentNode = firstNode;
	while (currentNode != nullptr && currentNode->element.first != key)
	{
		currentNode = currentNode->next;
	}

	if (currentNode != nullptr && currentNode->element.first == key)
	{
		return &currentNode->element;
	}

	return nullptr;
}

template<typename Key, typename Value>
void sortedChain<Key, Value>::erase(const Key& key)
{
	node_type* currentNode = firstNode;
	node_type* prevNode = nullptr;

	while (currentNode != nullptr && currentNode->element.first != key)
	{
		prevNode = currentNode;
		currentNode = currentNode->next;
	}

	if (currentNode != nullptr && currentNode->element.first == key)
	{
		if (currentNode == firstNode)
		{
			firstNode = firstNode->next;
		}
		else
		{
			prevNode->next = currentNode->next;
		}

		delete currentNode;
		dictionarySize--;
	}
}

template<typename Key, typename Value>
inline void sortedChain<Key, Value>::insert(const value_type& element)
{
	node_type* afterNode = firstNode;
	node_type* beforeNode = nullptr;

	while (afterNode != nullptr && afterNode->element.first < element.first)
	{
		beforeNode = afterNode;
		afterNode = afterNode->next;
	}

	if (afterNode != nullptr && afterNode->element.first == element.first)
	{
		// 替换旧数据
		afterNode->element = element;
		return;
	}

	auto newNode = new node_type({ element, afterNode });
	if (afterNode == firstNode)
	{
		firstNode = newNode;
	}
	else
	{
		beforeNode->next = newNode;
	}
	dictionarySize++;
}

以上是关于《数据结构算法与应用 —— C++语言描述》学习笔记 — 字典 — 链表实现的主要内容,如果未能解决你的问题,请参考以下文章

数据结构算法与应用c++语言描述--1.5习题总结

新书数据结构与算法(C++语言描述)

某公司自然语言处理算法笔试题

算法基础:差分算法及模板应用

《自然语言处理实战入门》 ---- 笔试面试题:机器学习基础(81-100)

算法题之端口(2017百度实习笔试题)