數據結構線性表單鏈表描述c++(二)

本例子是《數據結構與算法c++描述》中的線性表中單向鏈表(singly linked list)

 

單鏈表原理圖:

      first 爲頭節點  ,由於第一個節點 e1 的指針指向第二個節點 e2, e2 的指針指向e3 ,. . .,最後一個節點鏈接域爲 N U L L (或0 ),故這種結構也被稱作鏈(chain)。

 

單鏈表定義:

/*
數組結構中的線性錶鏈表樣式

對應書中代碼:數據結構算法與應用c++描述

程序編寫:比卡丘不皮

編寫時間:2020年6月30日 17:55:29
*/

#pragma once
#include "all_error.h"
#include <iostream>
using namespace std;

template<class T>
class Chain;  //聲明

template<class T>
class ChainIterator; //迭代器


template<class T>
class ChainNode
{
	friend Chain<T>;
	friend ChainIterator<T>;  //因爲要訪問private
private:
	T data;
	ChainNode<T> * link;
};


template<class T>
class Chain
{
	friend ChainIterator<T>;  //應爲要訪問private
public:
	Chain() { first = 0; last = first; };//初始化函數
	~Chain();                            //析構函數
	Chain(const Chain<T> & Val);  //複製構造函數
	bool IsEmpty() const { return first == 0; }   //判斷是不是空
	int Length()const;                    //鏈表的長度
	bool Find(int k, T &x) const;         //查找第k可的數據,沒有爲false
	int Search(const T & x) const;        //查找數據函數
	Chain<T>& Delete(int k, T & x);       //刪除某個數據
	Chain<T>& Insert(int k, const T & x); //插入數據
	void Output(ostream & out)const;  // 輸出接點數據
    //擴充鏈表
	void Erase();                   //刪除鏈表
	void Zero() { first = 0; }      //數據first 爲0
	Chain<T>& Append(const T & x);  //在最後添加
	Chain<T>& Reverse();   //元素次序的反轉  原地的反轉
	 //線性表交叉組合 參數一定不是自己的變量
	Chain<T>& Alternate(const Chain<T>& list1, const Chain<T>& list2);
	//有規律(元素從小到大排列)線性表的組合
	Chain<T>& Merge(const Chain<T>& list1, const Chain<T>& list2);
	//鏈表排序 排序法
	void sort();
	//線性表分割函數  交叉分割
	void Split(Chain<T>& list1, Chain<T>& list2);
	Chain<T> & operator =(const Chain<T>& x); //複製構造函數一定要同時寫這個
private:
	ChainNode<T> * first; //記錄頭指針
	ChainNode<T> * last; //記錄尾指針
};


//鏈表遍歷類
template<class T>
class ChainIterator
{
public:
	T * Initialize(const Chain<T> & c)
	{
		location = c.first;
		if (location)
		{
			return &location->data;
		}
		return 0;
	}

	T * Next()
	{
		if (!location)
		{
			return 0;
		}
		location = location->link;
		if (location)
		{
			return &location->data;
		}
		return 0;
	}
private:
	ChainNode<T> *location;
};

 有關  #include "all_error.h" 請看我博客中(1)的寫法:

 傳送門:https://blog.csdn.net/weixin_42126427/article/details/107013624

首先

template<class T>
class Chain;  //聲明

template<class T>
class ChainIterator; //迭代器

聲明,在對應類中friend class 是可以在對應鏈表中訪問private變量,所以要聲明。

析構函數:

template<class T>
 Chain<T>::~Chain()
{  
	ChainNode<T> * next;
	while (first)
	{
		next = first->link;
		delete first;
		first = next;
	}
}

循環對當前節點刪除,而不是隻是把first 給delete ,這裏一定要這樣刪除,不然類存泄漏.

Length()函數:

template<class T>
int Chain<T>::Length() const
{
	ChainNode<T> * current = first;
	int len = 0;
	while (current)
	{
		len++;
		current = current->link;
	}
	return len;

}

目的:計算鏈表的長度,返回長度。

Find(int k, T &x)函數:

template<class T>
 bool Chain<T>::Find(int k, T & x) const
{
	if (k < 1)
	{
		return false;
	}
	ChainNode<T> * current = first;
	int index = 1; //current的索引
	while (index < k && current)
	{
		current = current->link;
		index++;
	}
	if (current)
	{
		x = current->data;
		return true;
	}
	return false;
}

查找第k可的數據,沒有爲false,有數據,把值賦值給X;

搜索Search(const T & x):

//尋找x, 如果發現x 返回x的地址
//如果不在列表中,返回0
template<class T>
 int Chain<T>::Search(const T & x) const
{
	 ChainNode<T> *current = first;
	 int index = 1;
	 while (current && current->data != x)
	 {
		 current = current->link;
		 index++;
	 }
	 if (current)
	 {
		 return index;
	 }
	return 0;

}

 刪除 Delete(int k, T & x)

  刪除原理圖:

比如要刪除第4個,要找到第4個,然後讓3連接到5上,然後釋放4的節點; 

//把第k個元素取至x,然後從鏈表中刪除第 k個元素
 //如果不存在第 k個元素,則引發異常 OutOfBounds
 template<class T>
 Chain<T>& Chain<T>::Delete(int k, T & x)
 {
	 if (k < 1 || !first)
	 {
		 throw OutOfBounds();
	 }
     // p最終將指向第 k個節點
	 ChainNode<T> * p = first;
	 // 將p移動至第k個元素,並從鏈表中刪除該元素
	 if (k == 1)  //p已經指向第k個元素
	 {
		 first = first->link; //這樣就刪除了
	 }
	 else
	 {
		 ChainNode<T> * q = first;
		 for (int index = 1; index < k-1 && q; index++)
		 {
			 q = q->link;	 
		 }
		 if (!q || !q->link)
		 {
			 throw OutOfBounds();
		 }
		 //存在第k個元素 刪除對應元素
		 p = q->link;
		 if (p == last)
		 {
			 last = q;
		 }
		 q->link = p->link; //兩種可能性,p->link爲0, 那麼 q->link 就是0, 若有下個數據,就連接下個數據		 
	 }
	 x = p->data;
	 delete p;
	 return *this;
 }

輸出Output(ostream & out)

template<class T>
 void Chain<T>::Output(ostream & out) const
{
	 ChainNode<T> *current;
	 for (current = first; current; current = current->link)
	 {
		 out << current->data << " ";
	 }
}
 //重載<<
 template<class T>
 ostream & operator <<(ostream & out, const Chain<T> & x)
 {
	 x.Output(out);
	 return out;
 }

 插入Insert(int k, const T & x):

  插入原理圖:

當插入爲0爲在first插入最前,當爲k不是0在中間插入 ,OutOfBounds 這些錯誤在上面的傳送門中的頭文件中

//在第k個元素之後插入x
 //如果不存在第 k個元素,則引發異常OutOfBounds
 //如果沒有足夠的空間,則傳遞 Nomem異常
 template<class T>
Chain<T>& Chain<T>::Insert(int k, const T & x)
 {
	 if (k < 0)
	 {
		 throw OutOfBounds();
	 }
	 //p最終將指向第 k個節點
	 ChainNode<T> * p = first;
	 //將p移動至第k個元素
	 for (int index = 1; index < k && p; index++ )
	 {
		 p = p->link;
	 }
	 //不存在第k個元素
	 if (k > 0 && !p)
	 {
		 throw OutOfBounds();
	 }
	 //插入
	 ChainNode<T> * y = new ChainNode<T>;
	 y->data = x;
	 if (k) //在p之後插入
	 {
		 y->link = p->link;
		 p->link = y;
	 }
	 else
	 {
		 y->link = first;
		 first = y;
	 }
	 if (!y->link)
	 {
		 last = y; //記錄最後一個節點
	 }
	 return *this;
 }

刪除Erase():

 //刪除說有節點
 template<class T>
  void Chain<T>::Erase()
 {
	  ChainNode<T> *next;
	  while (first)
	  {
		  next = first->link;
		  delete first;
		  first = next;
	  }
 }

添加Append(const T & x):

  //在鏈表尾部添加一個節點
  template<class T>
  Chain<T>& Chain<T>::Append(const T & x)
  {
	  ChainNode<T> *y;
	  y = new ChainNode<T>;
	  y->data = x; y->link = 0;
	  if (first) //鏈表非空
	  {
		  last->link = y;
		  last = y;
	  }
	  else
	  {
		  first = last = y;
	  }
	  return *this;
  }

已上函數都是書上的源碼部分,對應的函數都已經實現,下面部分爲後面習題部分自己實現的函數功能,自己寫的測試可以通過,若你發現有些有問題歡迎在下面評論。

複製構造函數:

 //複製構造函數
 template<class T>
Chain<T>::Chain(const Chain<T>& Val)
 {
	 this->Erase(); //先清除所有數據
	 ChainNode<T> * next = Val.first;
	 while (next)
	 {
		 this->Append(next->data);
		 next = next->link;
	 }
 }

//重載 = 
  template<class T>
  Chain<T>& Chain<T>::operator=(const Chain<T>& x)
  {
	  this->Erase(); //先清除所有數據
	  ChainNode<T> * next = x.first;
	  while (next)
	  {
		  this->Append(next->data);
		  next = next->link;
	  }
	  return *this;
  }

反轉 Reverse():

//次序反轉
  template<class T>
   Chain<T>& Chain<T>::Reverse()
  {
	   if (first ==0 && first == last && first->link == 0)
	   {
		   return *this;
	   }
	   ChainNode<T> * current = first; //記錄位置
	   ChainNode<T> * pre = NULL;
	   last = first;  //頭變尾
	   //好好體會一下這個
	  while (first)
	  {
		  current = first;
		  first = first->link;
		  current->link = pre;
		  pre = current;
	  }
	  first = current; //current 爲頭指針
	  return *this;
  }

交叉線性Alternate(const Chain<T>& list1, const Chain<T>& list2)

 //交叉線性
   template<class T>
    Chain<T>& Chain<T>::Alternate(const Chain<T>& list1, const Chain<T>& list2)
   {
		if (this == &list1 && this == &list2)
		{
			cout << "參數不能是本身變量" << endl;
			return *this;
		}
		//清除前鏈表的數據
		this->Erase();
//		int length = list1.Length() + list2.Length();
		ChainNode<T> * pList1 = list1.first;
		ChainNode<T> * pList2 = list2.first;
		while (pList1 && pList2)
		{
			this->Append(pList1->data);
			this->Append(pList2->data);
			//指向下個
			pList1 = pList1->link;
			pList2 = pList2->link;
		}

		if (!pList1)   //pList1爲空的話 
		{
			while (pList2)
			{
				this->Append(pList2->data);
				pList2 =  pList2->link;
			}
		}
		else  
		{
			while (pList1)
			{
				this->Append(pList1->data);
				pList1 = pList1->link;
			}
		}
		return *this;
   }

合併有序數據Merge(const Chain<T>& list1, const Chain<T>& list2)

	//合併數據 前提數據必須是有序的。
   template<class T>
  Chain<T>& Chain<T>::Merge(const Chain<T>& list1, const Chain<T>& list2)
  {
	  //清除前鏈表的數據
	  if (this == &list1 && this == &list2)
	  {
		  cout << "參數不能是本身變量" << endl;
		  return *this;
	  }
	  //清除前鏈表的數據
	  this->Erase();
	  //獲取兩個鏈表的頭節點
	  ChainNode<T> * pList1 = list1.first;
	  ChainNode<T> * pList2 = list2.first;
	  while (pList1 && pList2)
	  {
		  if (pList1->data <= pList2->data)
		  {
			  this->Append(pList1->data);
			  pList1 = pList1->link;
		  }
		  else
		  {
			  this->Append(pList2->data);
			  pList2 = pList2->link;
		  }
	  }
	  //當其中一個鏈表爲空或者都爲空,開始下面處理
	  if (!pList1)
	  {
		  while (pList2)
		  {
			  this->Append(pList2->data);
			  pList2 = pList2->link;
		  }
	  }
	  else
	  {
		  while (pList1)
		  {
			  this->Append(pList1->data);
			  pList1 = pList1->link;
		  }
	  }  	
	  return *this;
  }

 排序sort()

 //選擇排序
  template<class T>
 void Chain<T>::sort()
  {
	 ChainNode<T> * p = first->link; //獲取
	 ChainNode<T> * min = first; //先獲取頭節點數據爲最小值

	 while(min)
	 {
		 p = min->link;
		 while (p)
		 {
			 if (min->data > p->data)
			 {
				 T temp = min->data;
				 min->data = p->data;
				 p->data = temp;
			 }
			 p = p->link;
		 }
		 min = min->link; //找到下一位
	 }

  }

這裏是書上沒有要求的,爲了後面測試方便。

拆分Split(Chain<T>& list1, Chain<T>& list2):

 template<class T>
void Chain<T>::Split(Chain<T>& list1, Chain<T>& list2)
 {
	//拆分的兩個變量不能爲當前拆分的值
	if (this == &list1 && this == &list2)
	{
		cout << "參數不能是本身變量" << endl;
		return ;
	}
	//若要拆分 list1 與 list2 首先要滯空
	list1.Erase();
	list2.Erase();
	ChainNode<T> * current = first;
	int index = 1;
	while (current)
	{
		if ((index & 1) == 1) //當爲奇數的時候
		{
			list1.Append(current->data);
	
		}
		else   //當爲偶數的時候
		{
			list2.Append(current->data);

		}
		//到達下個數據
		current = current->link;
		index++;
	}

 }

測試函數:

 void testChain()
  {
	  Chain<int> c;
	  cout << "新建鏈表:     " << endl;
	  cout << "鏈表是否爲空:   " << c.IsEmpty() << endl;
	  cout << "鏈表的長度:    " << c.Length() << endl;
	  cout << "鏈表爲:     " << c << endl << endl;
	  //測試插入函數
	  c.Insert(0, 1).Insert(1, 2).Insert(2, 3).Insert(3,4);
	  cout << "鏈表是否爲空:   " << c.IsEmpty() << endl;
	  cout << "鏈表的長度:    " << c.Length() << endl;
	  cout << "鏈表爲:     " << c << endl << endl;

	  int value = 0;
	  //find函數查找
	  c.Find(2, value);
	  cout << "測試Find()函數---c.Find(2,x):" << endl;
	  cout << "x爲:   " << value << endl << endl;

	  //查找函數
	  cout << "測試Search():" << endl;
	  cout << "元素0的位置爲:  " << c.Search(0) << endl;
	  cout << "元素2的位置爲:  " << c.Search(2) << endl << endl;

	  //測試刪除
	  cout << "開始刪除鏈表" << endl;
	  cout << "源鏈表:  " << c << endl;
	  c.Delete(2, value);
	  cout << "刪除的數據是:  " << value <<endl;
	  cout << "刪除後的鏈表:  " << c << endl;

	  //append 函數測試
	  c.Append(10);
	  cout << "插入後鏈表爲:  " << c << endl << endl;

	  //測試新遍歷器
	  int * va;
	  ChainIterator<int> d;
	  va = d.Initialize(c);
	  while (va)
	  {
		  cout << *va << " ";
		  va = d.Next();
	  }
	  cout << endl;

	  //測試構建複製函數
	  cout << "構建複製函數" << endl;
	  Chain<int> NewNum(c);
	  cout << "源鏈表:  " << c << endl;
	  cout << "新鏈表:  " << NewNum << endl;
	  //測試 =
	  cout << "=號賦值" << endl;
	  NewNum = c;
	  cout << "源鏈表:  " << c << endl;
	  cout << "新鏈表:  " << NewNum << endl;
	  cout << endl;

	  //測試反轉函數
	  cout << "源鏈表:        " << c << endl;
	  c.Reverse();
	  cout << "反轉後的鏈表:  " << c << endl;

	  cout << endl;

	  cout << " 測試Alternate 函數 " << endl;
	  Chain<int> testAl;
	  testAl.Alternate(NewNum,c);
	  cout << "NewNum 列表是:   " << NewNum << endl;
	  cout << "c 列表是:        " << c << endl;
	  cout << "testAl 列表是:   " << testAl << endl;
	  cout << endl;

	  //排序測試 選擇排序
	  cout << "排序測試" << endl;
	  cout << "c 列表是:        " << c << endl;
	  cout << "testAl 列表是:   " << testAl << endl;
	  c.sort();
	  testAl.sort();
	  cout << "排序後 c 列表是:        " << c << endl;
	  cout << "排序後 testAl 列表是:   " << testAl << endl;

	  //排序後測試Merge 函數
	  cout << "排序後測試Merge 函數" << endl;
	  Chain<int> testMerge;
	  testMerge.Merge(c, testAl);
	  cout << "Merge(c, testAl) 函數: " << testMerge << endl;
	  cout << endl;
		  //測試Split 
	  cout << "測試Split 函數" << endl;
      Chain<int> one, two;
	  cout << "拆分前testMerge數據:" << testMerge << endl;
	  cout << "拆分前one數據:      " << one << endl;
	  cout << "拆分前two數據:      " << two << endl;
	  testMerge.Split(one, two);
	  cout << "拆分後testMerge數據:" << testMerge << endl;
	  cout << "拆分後one數據:      " << one << endl;
	  cout << "拆分後two數據:      " << two << endl;
  }

主函數:

#include <iostream>
#include "listearLine.h"
#include "Chain.h"
using namespace std;

int main()
{
	//測試線性表數組
	//testLinearList();
	//測試線性錶鏈表
	testChain();
	return 0;
}

程序運行結果:

新建鏈表:
鏈表是否爲空:   1
鏈表的長度:    0
鏈表爲:

鏈表是否爲空:   0
鏈表的長度:    4
鏈表爲:     1 2 3 4

測試Find()函數---c.Find(2,x):
x爲:   2

測試Search():
元素0的位置爲:  0
元素2的位置爲:  2

開始刪除鏈表
源鏈表:  1 2 3 4
刪除的數據是:  2
刪除後的鏈表:  1 3 4
插入後鏈表爲:  1 3 4 10

1 3 4 10
構建複製函數
源鏈表:  1 3 4 10
新鏈表:  1 3 4 10
=號賦值
源鏈表:  1 3 4 10
新鏈表:  1 3 4 10

源鏈表:        1 3 4 10
反轉後的鏈表:  10 4 3 1

 測試Alternate 函數
NewNum 列表是:   1 3 4 10
c 列表是:        10 4 3 1
testAl 列表是:   1 10 3 4 4 3 10 1

排序測試
c 列表是:        10 4 3 1
testAl 列表是:   1 10 3 4 4 3 10 1
排序後 c 列表是:        1 3 4 10
排序後 testAl 列表是:   1 1 3 3 4 4 10 10
排序後測試Merge 函數
Merge(c, testAl) 函數: 1 1 1 3 3 3 4 4 4 10 10 10

測試Split 函數
拆分前testMerge數據:1 1 1 3 3 3 4 4 4 10 10 10
拆分前one數據:
拆分前two數據:
拆分後testMerge數據:1 1 1 3 3 3 4 4 4 10 10 10
拆分後one數據:      1 1 3 4 4 10
拆分後two數據:      1 3 3 4 10 10
請按任意鍵繼續. . .

本文例子程序:

鏈接:https://pan.baidu.com/s/1m8KlkXVzzNGKmKE9I9lwCA   提取碼:c2te

如果遇到什麼問題可以聯繫我,關注我的博客,一起加油學習吧。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章