数据结构与算法:顺序表和链表

目录

一、线性表

二、顺序表

 三、链表


一、线性表

        线性表( linear list )是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

        线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

        其中数组(某种程度上可以认为是顺序表)在物理空间上是连续存储的,而链表是由不同的小块通过某些“记忆”顺次连接存储的,这些小块或连续,或不连续,但是在逻辑上都呈线性。

二、顺序表

         顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般又可以分为静态顺序表和动态数据表:

        静态数据表是用一段长度固定的物理空间来对数据进行存储,数组长度是固定的。堆开辟的数组在特殊情景下,使用比较多,尤其是动态顺序表。

// 静态顺序表:通过宏来替换定义一个数组
#define N 10
struct Arr
{public:int n;int arr[N];
};
// 动态顺序表:在数组长度不确定的时候,采用堆空间来创建顺序表
class Arr
{public:Arr(int _n):n(_n),arr(new int[n]){}protected:int n;int* arr;
};

        显然,通过宏替换或者是栈上直接开辟的数组对于长度是已经锁死了的,当数据足够庞大的时候,N为10不够,这时需要修改为比较大的值。如果是本来就给N定为比较大的数,在面对较少的数据时就会显得浪费空间。所以在实践中,我们更推荐使用的是动态数组。动态数组可以实现在数组长度达到最大时扩容继续添加数据的作用。
        下面是一串采用C++实现的顺序表的代码:

(1)SeqList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cassert>
using namespace std;
class SeqList
{
public://初始化SeqList();//销毁~SeqList();//头插void PushFront(int data);//头删void PopFront();//尾插void PushBack(int data);//尾删void PopBack();//打印void Print();//寻找int* Find(int data);//插入void Insert(int pos, int data);//清除void Erase(int data);//检查容量void Check();
private:int* ptr;int index;int capacity;
};

(2)SeqList.cpp

#include "SeqList.h"
SeqList::SeqList()
{ptr = nullptr;index = 0;capacity = 0;
}
SeqList::~SeqList()
{if (ptr!=nullptr){delete[]ptr;ptr = nullptr;capacity = 0;index = 0;cout << "~SeqList Destroyed" << endl;}
}
void SeqList::PushFront(int data)
{Check();for (int i = index - 1; i >= 0; i--){ptr[i + 1] = ptr[i];}ptr[0] = data;index++;
}
void SeqList::PopFront()
{for (int i = 1; i < index; i++){ptr[i - 1] = ptr[i];}index--;
}
void SeqList::PushBack(int data)
{Check();assert(ptr);ptr[index++] = data;
}
void SeqList::PopBack()
{assert(index >= 0);index--;
}
void SeqList::Print()
{for (int i = 0; i < index; i++){cout << ptr[i] << " ";}cout << endl;
}
int* SeqList::Find(int data)
{for (int i = 0; i < index; i++){if (ptr[i] == data){return ptr + i;}}return nullptr;
}
void SeqList::Insert(int pos, int data)
{assert(pos > 0);Check();for (int i = index - 1; i >= pos - 1; i--){ptr[i + 1] = ptr[i];}ptr[pos-1] = data;index++;
}
void SeqList::Erase(int data)
{int flag = 1;for (int i = 0; i < index; i++){if (ptr[i] == data){for (int j = i; j <index; j++){ptr[j] = ptr[j + 1];}index--;flag = 0;}}if (flag){cout << "No Data" << endl;}
}
void SeqList::Check()
{if (ptr == nullptr || index == capacity){int new_capcaity = (ptr == nullptr ? 4 : capacity * 2);if (capacity != new_capcaity && capacity != 0){int* temp = new int[new_capcaity];for (int i = 0; i < index; i++){temp[i] = ptr[i];}delete[] ptr;ptr = temp;assert(ptr);capacity = new_capcaity;return;}ptr = new int[new_capcaity];capacity = new_capcaity;assert(ptr);}
}

        值得注意的是,如果使用模板template不能将函数定义与声明分离放在两个文件中,否则会导致很多的麻烦。

        顺序表在实现上是比较简单的,但是在某些方面上效率是比较低的。比如在进行增加数据的操作时,如果数组已经满了,那么需要开辟新空间,并且将原先的数据挪到新空间,并释放旧空间;然后在进行扩容的操作时,一般是扩2倍,导致资源利用率会随着数据的减少而减少;在进行头插和中间位置的插入时,时间复杂度为O(N)。

 三、链表

        链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

1.单向不循环链表

// 不带哨兵位的单向链表
#include <iostream>
#include <cassert>
using namespace std;
template<class T>
struct Node
{Node<T> *next;T data;Node(T _data) : data(_data),next(nullptr){}
};
template<class T>
class SLTList
{protected:Node<T> *pHead;public:SLTList():pHead(nullptr){}void push_back(T data){// 头为空就直接改变头指向if(!pHead)pHead = new Node<T>(data);// 头不为空就尾插else{Node<T> *tail = pHead;while(tail->next){tail = tail->next;}tail->next = new Node<T>(data);}}void pop_back(){// 头为空就直接返回if(!pHead)return;// 只有一个头结点,就释放该节点的权限并置空if(!pHead->next){delete pHead;pHead = nullptr;return;}Node<T> *fast = pHead;Node<T> *slow = pHead;while(fast->next){slow = fast;fast = fast->next;}slow->next = nullptr;delete fast;fast = nullptr;}void push_front(T data){// 头插 更改头指针的指向Node<T> *newNode = new Node<T>(data);newNode->next = pHead;pHead = newNode;}void pop_front(){// 如果头为空,就直接返回if(!pHead)return;// 只有一个头结点,那就删除头结点,并把pHead置为空if(!pHead->next){delete pHead;pHead = nullptr;return;}// 头非空,就创建一个新头指针,释放旧头指针,最后改变指向Node<T> *newHead = pHead->next;delete pHead;pHead = newHead;}void print(){// 打印输出链表的元素Node<T> *tail = pHead;while(tail){cout << tail->data << endl;tail = tail->next;}}Node<T>* find(T data){Node<T> *tail = pHead;while(tail){if(tail->data==data)return tail;tail = tail->next;}return nullptr;}void insert(Node<T>* pos,T data){if(!pos&&pos!=pHead)return;if(pos==pHead){push_front(data);}else{Node<T> *prePos = pHead;while(prePos->next!=pos){prePos = prePos->next;}Node<T> *newNode = new Node<T>(data);prePos->next = newNode;newNode->next = pos;}}void erase(Node<T>* pos){if(!pos)return;else{if(pos==pHead){pop_front();return;}Node<T> *prePos = pHead;while(prePos->next!=pos){prePos = prePos->next;}prePos->next = pos->next;delete pos;}}};
// 带哨兵位的单向链表
#include <iostream>
#include <cassert>
using namespace std;
// 带有哨兵位的单向链表
template <class T>
struct Node
{T data;Node<T> *next;Node(T _data = 0) : data(_data), next(nullptr) {}
};
template <class T>
class SLTList
{
protected:Node<T> *pHead;public:SLTList() : pHead(new Node<T>) {}~SLTList(){Node<T> *tail = pHead;while (tail->next){Node<T> *tmp = tail;tail = tail->next;delete tmp;tmp = nullptr;}}// 尾插void push_back(T _data){assert(pHead);Node<T> *tail = pHead;while (tail->next != nullptr){tail = tail->next;}tail->next = new Node<T>(_data);}// 尾删void pop_back(){Node<T> *fast = pHead;Node<T> *slow = pHead;while (fast->next != nullptr){slow = fast;fast = fast->next;}// 不能删除哨兵位if (fast != pHead)delete fast;slow->next = nullptr;}// 头插void push_front(T _data){if (pHead){Node<T> *oldTail = pHead->next;pHead->next = new Node<T>(_data);pHead->next->next = oldTail;}}// 头删void pop_front(){if (pHead && pHead->next){Node<T> *tmp = pHead->next;pHead->next = pHead->next->next;delete tmp;}}// 寻找_data的元素位置Node<T> *find(T _data){assert(pHead);Node<T> *pos = pHead->next;while (pos){if (pos->data == _data)return pos;pos = pos->next;}return nullptr;}// pos前面插入void insert(Node<T> *pos, T _data){assert(pHead);Node<T> *prePos = pHead;while (prePos->next != pos){prePos = prePos->next;}prePos->next = new Node<T>(_data);prePos->next->next = pos;}// 删除某个位置的元素void erase(Node<T> *pos){assert(pHead);if (pos){Node<T> *prePos = pHead;while (prePos->next != pos){prePos = prePos->next;}prePos->next = pos->next;delete pos;pos = nullptr;}}void print(){assert(pHead);Node<T> *tail = pHead->next;while (tail){cout << tail->data << endl;tail = tail->next;}}
};

2.带头双向循环链表

#include <iostream>
#include <cassert>
using namespace std;
struct ListNode
{ListNode *prev;ListNode *next;int data;
};
ListNode *ListCreate(int data)
{ListNode *newNode = new ListNode;if (newNode){newNode->data = data;newNode->prev = nullptr;newNode->next = nullptr;return newNode;}return nullptr;
}
void ListPrint(ListNode *pHead)
{assert(pHead);ListNode *tmp = pHead->next;cout << "哨兵位<=>";while (tmp != nullptr && tmp != pHead){cout << tmp->data << "<=>";tmp = tmp->next;}cout << endl;
}
void ListPushBack(ListNode *pHead, int data)
{assert(pHead);ListNode *tmp = ListCreate(data);if(pHead->next!=nullptr){ListNode *tail = pHead->prev;tail->next = tmp;tmp->prev = tail;tmp->next = pHead;pHead->prev = tmp;}else{pHead->next = tmp;pHead->prev = tmp;tmp->next = pHead;tmp->prev = pHead;}
}
void ListPopBack(ListNode*pHead)
{assert(pHead);if(pHead->next==nullptr||pHead->next==pHead->prev&&pHead->next==pHead){cout << "Doubly linked list is empty!" << endl;return;}ListNode* oldTail = pHead->prev;ListNode *newTail = oldTail->prev;newTail->next = pHead;pHead->prev = newTail;delete oldTail;oldTail = nullptr;
}
void ListPushFront(ListNode*pHead,int data)
{assert(pHead);if(pHead->next){ListNode *newHead = ListCreate(data);ListNode *oldHead = pHead->next;newHead->next = oldHead;newHead->prev = pHead;oldHead->prev = newHead;pHead->next = newHead;}else{ListNode *newHead = ListCreate(data);pHead->next = newHead;pHead->prev = newHead;newHead->next = pHead;newHead->prev = pHead;}
}
void ListPopFront(ListNode*pHead)
{assert(pHead);if(pHead->next==nullptr||pHead->next==pHead->prev&&pHead->next==pHead){cout << "Doubly linked list is empty!" << endl;return;}ListNode *oldHead = pHead->next;ListNode *newHead = oldHead->next;pHead->next=newHead;newHead->prev = pHead;delete oldHead;oldHead = nullptr;
}
ListNode* ListFind(ListNode* pHead, int x)
{assert(pHead);ListNode *tail = pHead->next;while(tail!=pHead){if(tail->data==x){return tail;}tail = tail->next;}return nullptr;
}
void ListInsert(ListNode* pos, int x)
{ListNode *newNode = ListCreate(x);ListNode *prevNode = pos->prev;prevNode->next = newNode;newNode->prev = prevNode;newNode->next = pos;pos->prev = newNode;
}
void ListErase(ListNode*pos)
{ListNode *prevNode = pos->prev;ListNode *nextNode = pos->next;prevNode->next = nextNode;nextNode->prev = prevNode;delete pos;pos = nullptr;
}

        对应STL里面的List: http://t.csdnimg.cn/xFva4 。

        单链表的实现相对容易一点,但是在实现尾插和尾删的效率上是比较低的。而双向带头循环链表虽然实现起来相对复杂,但是在使用上却展示出了许多的优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/43312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis框架学习笔记(一):MyBatis入门

1 MyBatis 介绍 1.1 官方文档 MyBatis 中文手册&#xff1a; &#xff08;1&#xff09;https://mybatis.org/mybatis-3/zh/index.html &#xff08;2&#xff09;https://mybatis.net.cn/ Maven 仓库&#xff1a; https://mvnrepository.com/ 仓库作用&#xff1a;需要…

(三)前端javascript中的数据结构之集合

集合的特点 1.无序 2.唯一性 3.不可重复 集合相对于前面几种数据结构&#xff0c;比较简单好理解&#xff0c;看看代码实现就能知道他的用法了 集合的创建 function MySet() {this.item {}; } MySet.prototype.has function (value) {return value in this.item; };//增 M…

编程范式之函数式编程

目录 前言1. 函数式编程的定义2. 函数式编程的特点2.1 纯函数2.2 不可变性2.3 高阶函数2.4 惰性求值 3. 函数式编程的应用场景3.1 并行计算3.2 数据分析3.3 Web开发 4. 函数式编程的优缺点4.1 优点4.2 缺点 5. 代表性的编程语言5.1 Haskell5.2 Scala5.3 Clojure 6. 示例代码结语…

HTML5新增的input元素属性:placeholder、required、autofocus、min、max等

HTML5 大幅度地增加与改良了 input 元素的属性&#xff0c;可以简单地使用这些属性来实现 HTML5 之前需要使用 JavaScript 才能实现的许多功能。 下面将详细介绍这些新增的 input 元素的属性。 属性说明属性说明placeholder在输入框显示描述性或提示性文本list为文本框添加选…

React+TS前台项目实战(二十七)-- 首页响应式构建之banner、搜索、统计模块布局

文章目录 前言一、 效果展示二、相关模块1. Statistic统计模块功能分析代码详细注释使用方式 2. Search搜索模块功能分析代码详细注释使用方式 3. banner模块功能分析代码详细注释使用方式 总结 前言 前面我们已经封装了这个项目基本要用到的全局组件了&#xff0c;现在就开始…

ScreenAI ——能理解从信息图表到用户界面的图像和文本算法解析

概述 论文地址&#xff1a;https://arxiv.org/pdf/2402.04615.pdf 信息图表&#xff08;图表、示意图、插图、地图、表格、文档布局等&#xff09;能够将复杂的数据和想法转化为简单的视觉效果&#xff0c;因此一直以来都被视为传播的重要元素。这种能力来自于通过布局和视觉线…

普通人必看!AI绘画商业变现全攻略(附教程)

大部分的设计师除了主业以外&#xff0c;都会利用空余时间去接单做副业。 单子包括但不限于产品/品牌LOGO、电商产品图设计、海报、室内设计图等等&#xff0c;单价在几十到上千不等 引起了我的思考&#xff0c;我们普通人有没有机会&#xff0c;也能像他们一样去接单赚钱吗&a…

pytorch- RNN循环神经网络

目录 1. why RNN2. RNN3. pytorch RNN layer3.1 基本单元3.2 nn.RNN3.2.1 函数说明3.2.2 单层pytorch实现3.2.3 多层pytorch实现 3.3 nn.RNNCell3.3.1 函数说明3.3.2 单层pytorch实现3.3.3 多层pytorch实现 4.完整代码 1. why RNN 以淘宝的评论为例&#xff0c;判断评论是正面…

matplotlib颜色对照表

matplotlib的色彩设置: #------------------------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------…

【JavaWeb】登录校验-会话技术(二)JWT令牌

JWT令牌 介绍 JWT全称&#xff1a;JSON Web Token &#xff08;官网&#xff1a;https://jwt.io/&#xff09; 定义了一种简洁的、自包含的格式&#xff0c;用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在&#xff0c;这些信息是可靠的。 简洁&#xff1a…

vue和react你怎么选择?

在选择Vue和React之间&#xff0c;其实没有一个绝对的“最佳选择”&#xff0c;因为这取决于你的项目需求、团队熟悉度、开发环境、以及你对这两个框架的个人偏好。下面是一些可以帮助你做出决策的因素&#xff1a; 1. 学习曲线 Vue&#xff1a;Vue的学习曲线相对平缓&#xf…

借助软件资产管理系统,优化Solidworks软件许可证管理

在当今数字化的企业环境中&#xff0c;软件许可证的有效管理对于业务的顺畅运行至关重要。然而&#xff0c;IT 运维部门常常面临着诸如用户部门 SW 许可证不够用、使用紧张等问题&#xff0c;而由于缺乏可靠的数据支持&#xff0c;难以准确判断许可证的短缺程度&#xff0c;这给…

MFC引用C#生成的dll,将dll放置到非exe程序目录,如何操作?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

信创:鲲鹏(arm64)+麒麟(kylin v10)离线部署k8s和kubesphere(含离线部署新方式)

本文将详细介绍&#xff0c;如何基于鲲鹏CPU(arm64)和操作系统 Kylin V10 SP2/SP3&#xff0c;利用 KubeKey 制作 KubeSphere 和 Kubernetes 离线安装包&#xff0c;并实战部署 KubeSphere 3.3.1 和 Kubernetes 1.22.12 集群。 服务器配置 主机名IPCPUOS用途master-1192.168.10…

【linux高级IO(二)】多路转接之select详解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux高级IO 1. 前言2. 初识s…

SCI丨返修一作+通讯

中科四区&#xff0c;JCR2 返修转让一作通讯&#xff0c;5个月左右录用 题目&#xff1a;通过机器学习算法XXXXXXXxxx混凝土力学性能的可靠方法

苍穹外卖--完善登录功能:进行MD5加密

目标 TODO&#xff1a;使用MD5加密方式对明文密码。 实现 password DigestUtils.md5DigestAsHex(password.getBytes());

Face_recognition实现人脸识别

这里写自定义目录标题 欢迎使用Markdown编辑器一、安装人脸识别库face_recognition1.1 安装cmake1.2 安装dlib库1.3 安装face_recognition 二、3个常用的人脸识别案例2.1 识别并绘制人脸框2.2 提取并绘制人脸关键点2.3 人脸匹配及标注 欢迎使用Markdown编辑器 本文基于face_re…

双向链表+Map实现LRU

LRU: LRU是Least Recently Used的缩写&#xff0c;即最近最少使用&#xff0c;是一种常用的页面置换算法&#xff0c;选择最近最久未使用的页面予以淘汰。 核心思想&#xff1a; 基于Map实现k-v存储&#xff0c;双向链表中使用一个虚拟头部和虚拟尾部&#xff0c;虚拟头部的…

BioXcell—InVivoMAb anti-West Nile/dengue virus E protein

研发背景&#xff1a; 西尼罗河病毒(WNV)是一种由蚊虫类介导传播的黄病毒&#xff0c;与引起人类感染性流行病的登革热病毒、黄热病病毒和日本脑炎病毒密切相关。 WNV和登革热病毒(DENV)同属黄病毒科&#xff08;Flaviviridae&#xff09;黄热病毒属&#xff0c;是具有小包膜单…