雙鏈表的基本實現與講解(C++描述)
雙鏈表
雙鏈表的意義
單鏈表相對於順序表,確實在某些場景下解決了一些重要的問題,例如在需要插入或者刪除大量元素的時候,它並不需要像順序表一樣移動很多元素,只需要修改指標的指向就可以了,其時間複雜度為 O(1) 但是這可是有前提的,那就是這一切都基於確定節點後,純粹考慮刪除和插入的情況下,但是如果我們仍未確定節點的位置,那麼單鏈表就會出現一些問題了,例如我們來看一下刪除這個操作
刪除操作
單鏈表:
對應圖中的節點,想要刪除第2個節點 a1 只需要 將首元結點的指標指向到第三個節點的地址去
但是問題就在於我們如何得到待刪除節點的前驅,也就是我們圖中的首元結點,我們給出兩種方法
- A:定位待刪除節點的同時,一直順便儲存當前節點的前驅
- B:刪除節點後,重新回到單鏈表表頭,定位到其指定前驅
但是無論我們選擇哪一種方法,指標的總移動數都會是 2n 次,而雙鏈表卻在這一型別問題上做出了很好的處理
雙鏈表:
單鏈表中之所以出現問題,就是因為各個節點只有一個指向後繼的指標域 next,只能向後移動查詢,一旦我們想要查詢前一節點,就變得很麻煩,所以雙鏈表就在每個節點前面增加一個指向前驅的指標域 prior,這樣我們就可以直接定位到我們的前一個節點了,這也就是雙鏈表
注意:為了統一運算,避免特殊情況的出現,我們也常常在尾部設定一個 “尾部頭結點” 其 next 指標域為空
線性表的抽象資料型別定義
我們在給出雙鏈表的定義之前我們還是需要先引入我們線性表的抽象資料型別定義
#ifndef _LIST_H_ #define _LIST_H_ #include<iostream> using namespace std; class outOfRange{}; class badSize{}; template<class T> class List { public: // 清空線性表 virtual void clear()=0; // 判空,表空返回true,非空返回false virtual bool empty()const=0; // 求線性表的長度 virtual int size()const=0; // 線上性表中,位序為i[0..n]的位置插入元素value virtual void insert(int i,const T &value)=0; // 線上性表中,位序為i[0..n-1]的位置刪除元素 virtual void remove(int i)=0; // 線上性表中,查詢值為value的元素第一次出現的位序 virtual int search(const T&value)const=0; // 線上性表中,查詢位序為i的元素並返回其值 virtual T visit(int i)const=0; // 遍歷線性表 virtual void traverse()const=0; // 逆置線性表 virtual void inverse()=0; virtual ~List(){}; }; /*自定義異常處理類*/ class outOfRange :public exception { //用於檢查範圍的有效性 public: const char* what() const throw() { return "ERROR! OUT OF RANGE.\n"; } }; class badSize :public exception { //用於檢查長度的有效性 public: const char* what() const throw() { return "ERROR! BAD SIZE.\n"; } }; #endif
雙鏈表型別的定義
#ifndef _SEQLIST_H_
#define _SEQLIST_H_
#include "List.h"
#include<iostream>
using namespace std;
template<class elemType>
//elemType為雙鏈表儲存元素型別
class doubleLinkList:public List<elemType> {
private:
//節點型別定義
struct Node {
//節點的資料域
elemType data;
//節點的兩個指標域
Node *prior, *next;
//兩個建構函式
Node(const elemType &value, Node *p = NULL, Node *n = NULL) {
data = value;
prior = p;
next = n;
}
Node():next(NULL), prior(NULL) {}
~Node(){}
};
//單鏈表的頭指標
Node *head;
//單鏈表的尾指標
Node *tail;
//單鏈表的當前長度
int curLength;
//返回指向位序為i的節點的指標
Node *getPosition(int i)const;
public:
doubleLinkList();
~doubleLinkList();
//清空單鏈表,使其成為空表
void clear();
//帶頭結點的單鏈表,判空
bool empty()const {return head -> next == NULL;}
//返回單鏈表的當前實際長度
int size()const {return curLength;}
//在位序i處插入值為value的節點表長增1
void insert(int i, const elemType &value);
//刪除位序為i的節點的值,表長減1
void remove(int i);
//查詢值為value的節點的第一次出現的位置
int search(const elemType &value)const;
//查詢值為value的節點的前驅的位序
int prior(const elemType&value)const;
//訪問位序為i的節點的值,0定位到首元結點
elemType visit(int i)const;
//遍歷單鏈表
void traverse()const;
//逆置單鏈表
void inverse();
//合併單鏈表
};
雙鏈表基本運算的實現
(一) 構造與解構函式
template <class elemType>
doubleLinkList<elemType>::doubleLinkList() {
//頭尾節點分別指向 頭結點和尾部頭結點
head = new Node;
tail = new Node;
head -> next = tail;
tail -> prior = head;
}
template <class elemType>
doubleLinkList<elemType>::~doubleLinkList() {
Node *p = head -> next, *tmp;
//頭結點的後繼是尾部頭結點
head -> next = tail;
//尾部頭結點的前驅是頭結點
tail -> prior = tail;
while(p != tail) {
tmp = p -> next;
delete p;
p = tmp;
}
curLength = 0;
}
(二) 查詢位序為i的節點的地址
template <class elemType>
typename doubleLinkList<elemType>::Node *doubleLinkList<elemType>::getPosition(int i) const {
Node *p = head;
int count = 0;
if(i < -1 || i > curLength)
return NULL;
while(count <= -1) {
p = p -> next;
count++;
}
return p;
}
(三) 查詢值為value的節點的位序
template <class elemType>
int doubleLinkList<elemType>::search(const elemType &value) const {
Node *p = head -> next;
int i = 0;
while(p != tail && p -> data != value) {
p = p -> next;
i++;
}
if(p == tail)
return -1;
else
return i;
}
(四) 插入元素
template <class elemType>
void doubleLinkList<elemType>::insert(int i, const elemType &value) {
Node *p, * tmp;
if(i < 0 || i > curLength)
throw outOfRange();
p = getPosition(i);
tmp = new Node(value, p -> prior, p);
//p原先的前驅的後繼指向tmp
p -> prior -> next = tmp;
//修改p的前驅為tmp
p -> prior = tmp;
++curLength;
}
(五) 刪除位序為i的節點
template <class elemType>
void doubleLinkList<elemType>::remove(int i) {
Node *p;
if(i < 0 || i > curLength)
throw outOfRange();
p = getPosition(i);
p -> prior -> next = p -> next;
p -> next -> prior = p -> prior;
delete p;
--curLength;
}
(六) 訪問位序為 i的節點的值
template <class elemType>
elemType doubleLinkList<elemType>::visit(int i) const {
//visit 不嫩直接用getPosition判斷範圍是否合法,因為其範圍為[-1,curLength]
if(i < 0 || i > curLength -1)
throw outOfRange();
//合法以後
Node *p = getPosition(i);
return p -> data;
}
(七) 遍歷雙鏈表
template <class elemType>
void doubleLinkList<elemType>::traverse() const {
Node *p = head -> next;
cout << "traverse: ";
while(p != tail) {
cout << p -> data << " ";
p = p -> next;
}
cout << endl;
}
(八) 遍歷雙鏈表
template <class elemType>
void doubleLinkList<elemType>::inverse() {
Node *tmp, *p = head -> next;
//構成雙空連結串列
head -> next = tail;
tail -> prior = head;
while(p != tail) {
tmp = p -> next;
p -> next = head -> next;
p -> prior = head;
head -> next -> prior = p;
head -> next = p;
p = tmp;
}
}
結尾:
如果文章中有什麼不足,或者錯誤的地方,歡迎大家留言分享想法,感謝朋友們的支援!
如果能幫到你的話,那就來關注我吧!如果您更喜歡微信文章的閱讀方式,可以關注我的公眾號
在這裡的我們素不相識,卻都在為了自己的夢而努力 ❤
一個堅持推送原創開發技術文章的公眾號:理想二旬不止
相關推薦
雙鏈表的基本實現與講解(C++描述)
雙鏈表 雙鏈表的意義 單鏈表相對於順序表,確實在某些場景下解決了一些重要的問題,例如在需要插入或者刪除大量元素的時候,它並不需要
線性表——順序表的實現與講解(C++描述)
線性表 引言 新生安排體檢,為了 便管理與統一資料,學校特地規定了排隊的方式,即按照學號排隊,誰在前誰在後,這都是規定好的,所以誰
雙鏈表程式碼實現和講解
1、什麼是連結串列 請移步看我前一篇https://www.cnblogs.com/han200113/p/11549338.html 2、雙鏈表和單鏈表有什麼不同? &n
鄰接表的建立與輸出(C語言)
鄰接表是圖的常用儲存結構之一,它很好的解決了鄰接矩陣佔用空間較大的問題。 鄰接表用到了兩個結構體,一個是頂點表,包括點的序號和連線此起點的第一條邊。一個是邊表,包括連線此邊的終點和對應之前起點的下一條邊。 初始化鄰接表時,先將定點表賦值,並把指標指向NULL。再將輸入的資料
鏈表翻轉的圖文講解(遞歸與叠代兩種實現)
art space rst 方式 sin 非遞歸實現 class 添加 技術 鏈表的翻轉是程序員面試中出現頻度最高的問題之一,常見的解決方法分為遞歸和叠代兩種。最近在復習的時候,發現網上的資料都只告訴了怎麽做,但是根本沒有好好介紹兩種方法的實現過程與原理。所以我覺得有必要
數據結構 鏈表_雙向鏈表的實現與分析
des list key src eof 定義 bsp tdi end 雙向鏈表的實現與分析 雙向鏈表的組成 :1、數據成員;2、指向下一個元素的next指針;3、指向前一個元素的prev指針。 數據結構DListElmt:代表雙向鏈表中的單個元素(節點)。 數據結構D
資料結構之雙鏈表基本操作
/*刪除節點時,無須找到要刪除節點的前驅節點,直接對目標節點進行刪除操作。*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<malloc.h>#define OK 1#define E
資料結構基礎4_雙鏈表的實現
typedef int ElemType; typedef int Status; #define true 1 #define false 0 typedef struct Dnode{ ElemType data; struct Dnod
資料結構與演算法(C語言) | 線性表(順序儲存、鏈式儲存)
線性表是最常用最簡單的線性結構 線性結構具有以下基本特徵: 線性結構是一個數據元素的有序(次序)集(處理元素有限)。若該集合非空,則 1)必存在唯一的一個“第一元素”; 2)必存在唯一的一個“最後元素”; 3)除第一元素之外,其餘每個元素均有唯一的前
C和指標 十二章 雙鏈表沒有實現
這一章主要講了連結串列; 單鏈表和雙鏈表,由於某些原因,只實現了單鏈表;雙鏈表等我看到後邊資料結構再回來補上去 #include <stdio.h> #include <stdlib.h> //這段程式碼參考了c和指標以及深入淺出C語言程式設計連結
圖解雙鏈表(Java實現)
>原創公眾號:[bigsai](https://mp.weixin.qq.com/s/IW_GNK254ijIuuupjJsKCA) > 文章已收錄在 [全網都在關注的資料結構與演算法學習倉庫](https://github.com/javasmall/bigsai-algorithm) #
習題2.5 兩個有序鏈表序列的合並(15 分)浙大版《數據結構(第2版)》題目集
merge 其中 接口 cnblogs oid color 給定 style bsp 本題要求實現一個函數,將兩個鏈表表示的遞增整數序列合並為一個非遞減的整數序列。 函數接口定義: List Merge( List L1, List L2 ); 其中Lis
7-15 兩個有序鏈表序列的合並(20 分)
一行 htm text all color div 兩個 turn nbsp 已知兩個非降序鏈表序列S1與S2,設計函數構造出S1與S2的並集新非降序鏈表S3。 輸入格式: 輸入分兩行,分別在每行給出由若幹個正整數構成的非降序序列,用−1表示序列的結
習題2.5 兩個有序鏈表序列的合並(15 分)<有疑問?L1 L2 沒辦法變空>
測試 bsp rto 標準 結構定義 tail ret 回歸 一個 習題2.5 兩個有序鏈表序列的合並(15 分) 本題要求實現一個函數,將兩個鏈表表示的遞增整數序列合並為一個非遞減的整數序列。 函數接口定義: List Merge( List L1, List
Octavia 的實現與分析(OpenStack Rocky)
目錄 文章目錄 目錄 Octavia 基本物件概念 基本使用流程 軟體架構 服務程序清單 程式碼結構 loadbalancer 建立流程分析 network_tasks.A
資料結構與演算法(Java描述)-15、稀疏矩陣以及稀疏矩陣的三元組實現
一、稀疏矩陣 對一個m×n的矩陣,設s為矩陣元素個數的總和,有s=m*n,設t為矩陣中非零元素個數的總和,滿足t<<s的矩陣稱作稀疏矩陣。符號“<<”讀作小於小於。簡單說,稀疏矩陣就是非零元素個數遠遠小於元素個數的矩陣。相對於稀疏矩陣來說,一個不稀疏的矩陣也稱作稠密矩陣。
資料結構——單鏈表實現及操作(c語言)
#include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #d
oracle表的建立與管理 (學習筆記)
複製表:create table myemp1 as select* from myemp;此類語法只有Oracle才支援.--表示註釋資料增加:insert into 表名稱[列名稱1,...] values(值1,...);資料修改:update 表名稱 set 欄位1=
資料結構與演算法(C語言) | 二叉排序樹
二叉排序樹的定義—— 二叉排序樹 ( Binary Sort Tree) 或者為空;或者是具有如下特性的二叉樹: (1)若根的左子樹不空,則左子樹上所有結點的關鍵字均小於根結點的關鍵字; (2)若
資料結構與演算法(Java描述)-20、圖、圖的鄰接矩陣、有向圖的廣度優先遍歷與深度優先遍歷
一、圖的基本概念圖:是由結點集合及結點間的關係集合組成的一種資料結構。結點和邊:圖中的頂點稱作結點,圖中的第i個結點記做vi。有向圖: 在有向圖中,結點對<x ,y>是有序的,結點對<x,y>稱為從結點x到結點y的一條有向邊,因此,<x,y>與<y,x>是兩條不同的邊。有向圖