想想自己好像並沒有真正實現過數據結構裏面的各類結構和算法,便打算動手用c++實現數據結構。這一篇是實現雙鏈表,雙鏈表較之於單鏈表,前者可以雙向連接,既可以向前,也可以向後;而單鏈表的方向是單一的、固定的。好的,話不多說,直接上乾貨吧。
首先,聲明雙鏈表節點 DNode,每個節點都有一個字段表示當前節點的值、有一個前驅字段來指向上一個節點、有一個後繼字段指向下一個節點。默認構造和拷貝構造的區別是,如果初始化 DNode 時,如果沒有指定value、prev、next 的話就使用默認構造函數 DNode();如果初始化時指定了 value 、prev、next 的值,就會使用拷貝構造函數DNode(T t, DNode * prev, DNode * next)。
//聲明雙鏈表節點的模板類結構體
template<class T>
struct DNode
{
public:
T value;
DNode * prev;
DNode * next;
public:
//默認構造函數
DNode(){}
//拷貝構造函數
DNode(T t, DNode * prev, DNode * next){
this->value = t;
this->prev = prev;
this->next = next;
}
};
template<class T>是 C++ 模板編程的標識,T表示數據類型,進行實例化時可以自己定義爲int,float,char等數據類型,也可以是結構體等自定義數據類型。
模板編程,顧名思義,是一個模板,可以給多個類或結構體套用的模板。我們在使用函數或類或結構體是時,都必須知名數據類型是什麼,比如void print(int a,int b)這個函數只能傳入int數據類型的參數;而void print(T a,T b)可以是任意類型。
定義好雙鏈表節點結構後,繼續定義雙鏈表結構
//聲明雙鏈表的模板類
template<class T>
class DoubleLink{
public:
DoubleLink();
~DoubleLink();
int size();
int is_empty();
T get(int index);
T get_first();
T get_last();
int insert(int index, T t);
int insert_first(T t);
int append_last(T t);
int del(int index);
int delete_first();
int delete_last();
private:
int count;
DNode<T> * phead;
DNode<T> * get_node(int index);
};
雙鏈表時由每一個一開始定義的 DNode 節點連接起來的,count 表示雙鏈表中 DNode 節點的數量,phead表示表頭結點,get_node(int index)函數用來獲取 index 位置的節點,返回的是 DNode * 類型。
- DoubleLink():構造函數,對DoubleLink()的定義如下:
template<class T>
DoubleLink<T>::DoubleLink():count(0){
//創建表頭
phead = new DNode<T>();
phead->prev = phead->next = phead;
}
DoubleLink<T>::DOubleLink():count(0):使用了Lambda表達式來初始化 count,賦值爲0。
調用拷貝構造函數,即實例化對象時,會自動創建一個表頭節點phead,phead的前驅和後繼都是它本身。
- ~DoubleLink():析構函數,用來銷燬對象
template<class T>
DoubleLink<T>::~DoubleLink(){
//刪除所有節點
DNode<T> * ptmp;
DNode<T> * pnode = phead->next;
while (pnode != phead){
ptmp = pnode;
pnode = pnode->next;
delete ptmp;
}
delete phead;
phead = NULL;
}
析構函數的定義與構造函數相反,用來銷燬對象,銷燬每一個節點。
- int size():獲取雙鏈表中節點的數量
//獲取雙鏈表節點數
template<class T>
int DoubleLink<T>::size(){
return count;
}
- int is_empty():判斷是否爲空函數
//判斷雙鏈表是否爲空
template<class T>
int DoubleLink<T>::is_empty(){
return count == 0;
}
如果count不爲0,則返回0,否則返回1。
- T get_node(int index):獲取 index 位置的節點,返回的是節點類型
//獲取index位置的節點
template<class T>
DNode<T>* DoubleLink<T>::get_node(int index){
if (index < 0 || index >= count){
std::cout << "get node failed! the index in out of bound!" << std::endl;
return NULL;
}
//正向查找
if (index <= count / 2){
int i = 0;
DNode<T> * pindex = phead->next;
for (i; i < index; i++){
pindex = pindex->next;
}
return pindex;
}
//反向查找
DNode<T> *rpindex = phead->prev;
for (int j = 0; j < count - index - 1; j++){
rpindex = rpindex->prev;
}
return rpindex;
}
首先判斷 index 是否爲有效位置,如果小於0或者超過雙鏈表實際長度,輸出錯誤信息。
若 Index 爲有效位置,如果Index在鏈表的前半部分也就是在鏈表中間節點位置之前,就從鏈表頭部開始遍歷,一直到index位置;
如果index 在鏈表後半部分,也就是在鏈表中間節點位置之後,就從鏈表尾部反向遍歷,一直到index位置。
之所以要先判斷index在中間節點位置之前還時之後,主要是爲能夠減少遍歷節點的數量,減少開支。
T get(int index):獲取index位置節點的值:
//獲取index位置的節點的值
template<class T>
T DoubleLink<T>::get(int index){
return get_node(index)->value;
}
T get_first():獲取鏈表第一個結點的值(不是頭節點)。
//獲取第一個節點的值
template<class T>
T DoubleLink<T>::get_first(){
return get_node(0)->value;
}
T get_last():獲取鏈表最後一個節點的值。直接調用get_node(int index)函數獲取最後一個節點的值
//獲取最後一個節點的值
template<class T>
T DoubleLink<T>::get_last(){
return get_node(count - 1)->value;
}
int insert(int index,T t):將 t 插入到鏈表index的位置上。
//將節點插入到第index位置之前
template<class T>
int DoubleLink<T>::insert(int index, T t){
if (index == 0){
return insert_first(t);
}
DNode<T>*pindex = get_node(index);
DNode<T>*pnode = new DNode<T>{ t, pindex->prev, pindex };
pindex->prev->next = pnode;
pindex->prev = pnode;
count++;
return 0;
}
int insert_first(T t):將 t 插入到鏈表的一個節點位置。
//將節點插入到第一個節點處
template<class T>
int DoubleLink<T>::insert_first(T t){
DNode<T> *pnode = new DNode<T>{ t, phead, phead->next };
phead->next->prev = pnode;
phead->next = pnode;
count++;
return 0;
}
int append_last(T t):將 t 加到鏈表尾部。注意鏈表連接的變化。
//將節點插入到最後一個節點處
template<class T>
int DoubleLink<T>::append_last(T t){
DNode<T>*pnode = new DNode<T>{ t, phead->prev, phead };
phead->prev->next = pnode;
phead->prev = pnode;
count++;
return 0;
}
int del(int index):刪除 index位置的節點:
//刪除index位置的節點
template<class T>
int DoubleLink<T>::del(int index){
DNode<T> * pindex = get_node(index);
pindex->next->prev = pindex->prev;
pindex->prev->next = pindex->next;
delete pindex;
count--;
return 0;
}
每次插入或者刪除鏈表節點,count 都要加1或者減1.
int delete_first():刪除第一個節點。
//刪除第一個節點
template<class T>
int DoubleLink<T>::delete_first(){
return del(0);
}
int delete_last():刪除最後一個節點。
//刪除最後一個節點
template<class T>
int DoubleLink<T>::delete_last(){
return del(count - 1);
}
鏈表的聲明和定義要在同一個文件裏,聲明和定義分文件的話,會報錯的,因爲模板編程不支持這樣,具體可以參考
下面是頭文件的全部代碼:
// DoubleLink.h
#include <iostream>
//聲明雙鏈表節點的模板類結構體
template<class T>
struct DNode
{
public:
T value;
DNode * prev;
DNode * next;
public:
//默認構造函數
DNode(){}
//拷貝構造函數
DNode(T t, DNode * prev, DNode * next){
this->value = t;
this->prev = prev;
this->next = next;
}
};
//聲明雙鏈表的模板類
template<class T>
class DoubleLink{
public:
DoubleLink();
~DoubleLink();
int size();
int is_empty();
T get(int index);
T get_first();
T get_last();
int insert(int index, T t);
int insert_first(T t);
int append_last(T t);
int del(int index);
int delete_first();
int delete_last();
private:
int count;
DNode<T> * phead;
DNode<T> * get_node(int index);
};
//定義雙鏈表的模板類
template<class T>
DoubleLink<T>::DoubleLink():count(0){
//創建表頭
phead = new DNode<T>();
phead->prev = phead->next = phead;
}
template<class T>
DoubleLink<T>::~DoubleLink(){
//刪除所有節點
DNode<T> * ptmp;
DNode<T> * pnode = phead->next;
while (pnode != phead){
ptmp = pnode;
pnode = pnode->next;
delete ptmp;
}
delete phead;
phead = NULL;
}
//獲取雙鏈表節點數
template<class T>
int DoubleLink<T>::size(){
return count;
}
//判斷雙鏈表是否爲空
template<class T>
int DoubleLink<T>::is_empty(){
return count == 0;
}
//獲取index位置的節點
template<class T>
DNode<T>* DoubleLink<T>::get_node(int index){
if (index < 0 || index >= count){
std::cout << "get node failed! the index in out of bound!" << std::endl;
return NULL;
}
//正向查找
if (index <= count / 2){
int i = 0;
DNode<T> * pindex = phead->next;
for (i; i < index; i++){
pindex = pindex->next;
}
return pindex;
}
//反向查找
DNode<T> *rpindex = phead->prev;
for (int j = 0; j < count - index - 1; j++){
rpindex = rpindex->prev;
}
return rpindex;
}
//獲取index位置的節點的值
template<class T>
T DoubleLink<T>::get(int index){
return get_node(index)->value;
}
//獲取第一個節點的值
template<class T>
T DoubleLink<T>::get_first(){
return get_node(0)->value;
}
//獲取最後一個節點的值
template<class T>
T DoubleLink<T>::get_last(){
return get_node(count - 1)->value;
}
//將節點插入到第index位置之前
template<class T>
int DoubleLink<T>::insert(int index, T t){
if (index == 0){
return insert_first(t);
}
DNode<T>*pindex = get_node(index);
DNode<T>*pnode = new DNode<T>{ t, pindex->prev, pindex };
pindex->prev->next = pnode;
pindex->prev = pnode;
count++;
return 0;
}
//將節點插入到第一個節點處
template<class T>
int DoubleLink<T>::insert_first(T t){
DNode<T> *pnode = new DNode<T>{ t, phead, phead->next };
phead->next->prev = pnode;
phead->next = pnode;
count++;
return 0;
}
//將節點插入到最後一個節點處
template<class T>
int DoubleLink<T>::append_last(T t){
DNode<T>*pnode = new DNode<T>{ t, phead->prev, phead };
phead->prev->next = pnode;
phead->prev = pnode;
count++;
return 0;
}
//刪除index位置的節點
template<class T>
int DoubleLink<T>::del(int index){
DNode<T> * pindex = get_node(index);
pindex->next->prev = pindex->prev;
pindex->prev->next = pindex->next;
delete pindex;
count--;
return 0;
}
//刪除第一個節點
template<class T>
int DoubleLink<T>::delete_first(){
return del(0);
}
//刪除最後一個節點
template<class T>
int DoubleLink<T>::delete_last(){
return del(count - 1);
}
下面做個測試:
#include "DoubleLink.h"
#include <string>
void int_test(){
DoubleLink<int> *pdlink = new DoubleLink<int>();
pdlink->insert_first(20);
pdlink->append_last(10);
pdlink->insert(1, 90);
std::cout << "雙鏈表第一個節點爲:" << pdlink->get_first() << std::endl;
std::cout << "雙鏈表最後一個節點爲:" << pdlink->get_last() << std::endl;
pdlink->delete_first();
pdlink->delete_last();
pdlink->del(0);
if (pdlink->is_empty()){
std::cout << "DoubleLink is empty" << std::endl;
}
else{
std::cout << "DoubleLink is not empty" << std::endl;
}
for (int i = 0; i < pdlink->size(); i++){
std::cout << pdlink->get(i) << std::endl;
}
}
void string_test(){
DoubleLink<std::string> *pslink = new DoubleLink<std::string>();
//pslink->append_last("Hello world! key!");
pslink->insert_first("Hello world! key!");
pslink->insert(0, "My name is zky");
pslink->append_last("呵呵");
for (int i = 0; i < pslink->size(); i++){
std::cout << pslink->get(i) << std::endl;
}
}
void object_test(){
struct stu{
int id;
char name[20];
};
static struct stu arr_stu[] = {
{0,"小強"},
{ 1, "小李" },
{2,"小剛"}
};
DoubleLink<stu> *polink = new DoubleLink<stu>();
polink->insert(0, arr_stu[0]);
polink->insert_first(arr_stu[2]);
for (int i = 0; i < polink->size(); i++){
std::cout << polink->get(i).id << " " << polink->get(i).name << std::endl;
}
}
int main(){
//int_test();
//string_test();
object_test();
std::cin.get();
return 0;
}
好了,雙鏈表的學習就到這了,後續會學習其他數據結構。