iT邦幫忙

2021 iThome 鐵人賽

DAY 8
1
Software Development

程式菜鳥自學C++資料結構演算法系列 第 8

[Day08]程式菜鳥自學C++資料結構演算法 – 鏈結串列實作應用之二

  • 分享至 

  • xImage
  •  

前言:昨天簡單實作了鏈結串列,今天要來介紹進階一點的應用,第一個是利用之前寫的get()和set()進行下標元素符號的讀寫,第二個是要做鏈結串列的反轉。
https://ithelp.ithome.com.tw/upload/images/20210922/20140187RDQ0SvSTeH.png
甚麼是下標?下標是一個可以快速存取及設置值的方式,光看中文可能不太清楚什麼意思,其實在前面章節介紹陣列及鏈結串列時已經有接觸過,緊接在名稱後的中括號[],括號內填入陣列的索引值,就可存取或設置值。

前言講完了就直接開始吧,打開之前鏈結串列的檔案就好,不用再創一個新的!

一. 下標運算符讀寫:
要先寫一個判斷鏈結串列大小的函示。
(上一篇忘記補上了,真對不起(≧д≦ヾ))
https://ithelp.ithome.com.tw/upload/images/20210922/20140187h6Y8pRnGEc.png

再來就是下標運算的讀寫了,因為和get()都是屬於讀寫類型,所以程式碼相似度很高。
https://ithelp.ithome.com.tw/upload/images/20210922/20140187j9GyElZbR4.png

接著就是輸出結果
https://ithelp.ithome.com.tw/upload/images/20210922/20140187OyleYri46T.png
可以看到下標是1的Blue改成Purple了(Yello的下標是0)。
https://ithelp.ithome.com.tw/upload/images/20210922/20140187l09EYwbOGP.png
這樣就完成下標的讀寫了,這裡用字串呈現比較清楚,下一篇會解釋怎麼使用字串顯示資料。

二. 鏈結串列的反轉:
https://ithelp.ithome.com.tw/upload/images/20210922/20140187DnL9frIYAl.png
https://ithelp.ithome.com.tw/upload/images/20210922/20140187w68kDPRCYE.png

可以看到鏈結串列已經反轉成功了。
https://ithelp.ithome.com.tw/upload/images/20210922/20140187Z8siin5nXf.png

今日小結:這樣就討論完陣列跟鏈結串列了,辛苦各位了。但還有一段路要走,今天的內容比較少,偶爾這樣也不錯嘛( ̄▽ ̄)~*最後再用表格的方式幫大家複習陣列和鏈結串列的特性比較,明天就要進入下一個單元「堆疊」了。

陣列 鏈結串列
靜態資料結構 動態資料結構
必須先宣告容量和資料數量 不必事先宣告
程式執行時容量大小已經固定 程式執行時才做記憶體配置
較節省記憶體,無額外的鏈結空間 每一筆資料都必須多一個指標欄位連接,較浪費記憶體
加入、刪除資料時需大量移動資料位置 加入、刪除只需改變指標的指向
可運用索引值直接存取 無索引值,不能直接存取

最後在附上這一篇和上一篇的程式碼

template<typename T>
class LinkList {
	struct LNode //建立左節點結構
	{
		T data;
		LNode* next; //指向下一個元素的指針變量
	};
	LNode* head; //指向最前端的指針變量
public:
	LinkList() { //創建空的鏈結串列,並對head指針初始化
		head = new LNode();
		head->next = 0;
	}
	bool get(int i, T e) { //讀取鏈結串列的資料
		if (i <= 0) return false; //i結點小於0則錯誤
		LNode* p = head;  
		int j = 0; //j為現在訪問的元素
		while (p && j < i) {
			p = p->next;
			j++;
		}
		if (!p) return false; //如果p是空指針則錯誤
		e = p->data; return true; //如果p找到i號節點的數據,則放入引用變量e
	}
	bool set(int i, T e) { //修改鏈結串列的資料,和get概念類似
		if (i <= 0) return false; 
		LNode* p = head;
		int j = 0; 
		while (p && j < i) {
			p = p->next;
			j++;
		}
		if (!p) return false; 
		p->data = e; return true;
	}
	bool insert(int i, T e) { //插入元素至節點內
		if (i <= 0) return false;
		LNode* p = head;
		int j = 0;
		while (p && j < i-1) {
			p = p->next;
			j++;
		}
		if (!p) return false;
		LNode* s = new LNode(); //s為臨時的指針變量,存放新插入的元素
		s->data = e;
		s->next = p->next; //使p指向修改後的新節點
		p->next = s;
		return true;
	}
	bool remove(int i) { //刪除節點的函式,原理和insert相似
		if (i <= 0) return false;
		LNode* p = head;
		int j = 0;
		while (p && j < i) {
			p = p->next;
			j++;
		}
		if (!p) return false;
		LNode* q = p->next;
		p->next = q->next; 
		delete q;
		return true;
	}
	bool insert_front(T e) { //插入節點在鏈結串列最前端
		LNode* s = new LNode(); 
		if (!s) return false;
		s->data = e;
		s->next = head->next; 
		head->next = s;
		return true;
	}
	bool remove_front() { //刪除第一個節點
		LNode* q = head->next;
		head->next = q->next;
		delete q;
		return true;
	}
	bool push_back(T e) { //插入節點在鏈結串列最末端
		LNode* p = head;
		while (p->next)
			p = p->next;
		p->next = new LNode();
		if (!p->next) return false;
		p->next->next = 0; //p->next表示現在的尾節點,而p->next的下一個節點要成為新的尾節點
		p->next->data = e; //同時要把資料放進去
		return true;
	}
	void traverse(void (*fp)(T& e)) { //編寫遍歷操作,fp為函數指針,對p指向的data進行處理
		LNode* p = head->next;
		while (p) {
			fp(p->data);
			p = p->next;
		}
	}
	void converse() {
		LNode* p = head->next;
		head->next = 0;
		while (p) { //前插法到頭節點後
			LNode* q = p->next; //保存下一個節點位址
			p->next = head->next; //指向新鏈結串列的首節點
			head->next = p;
			p = q;
		}
	}

	int size() { //判斷鏈結串列大小
		LNode* p = head->next;
		int j = 0;
		while (p) {
			j++;
			p = p->next;
		}
		return j;
	}
	
	T& operator[](int i) {//編寫下標運算函式,並傳回引用變量T
		if (i < 0) throw "下標異常";
		LNode* p = head; int j = -1;
		while (p && j < i) {
			p = p->next;
			j++;
		}
		if (!p) throw "下標異常";
		return p->data;
	}

};
#include<iostream>
template<typename T>
void Print(T &ch) {
	std::cout << ch << " ";
}

主程式碼

#include <string>
using std::string;
int main(){
	LinkList<char> list;
	list.push_back('A'); list.traverse(Print); std::cout << std::endl;
	list.push_back('B'); list.traverse(Print); std::cout << std::endl;
	list.insert_front('C'); list.traverse(Print); std::cout << std::endl;
	list.insert_front('D'); list.traverse(Print); std::cout << std::endl;
	list.insert(3,'E'); list.traverse(Print); std::cout << std::endl;
	list.set(2, 'F'); list.traverse(Print); std::cout << std::endl;


	LinkList<string> list2;
	list2.push_back("Blue");
	list2.push_back("Red");
	list2.insert_front("Yello");
	list2.push_back("Black");
	list2.traverse(Print); std::cout << std::endl;
	
	list2[1] = "Purple";
	for (int i = 0; i < list2.size(); i++)
		std::cout << list2[i] << " ";
	std::cout << std::endl;

	list2.converse();
	list2.traverse(Print); std::cout << std::endl;

}

上一篇
[Day07]程式菜鳥自學C++資料結構演算法 – 鏈結串列實作應用
下一篇
[Day09]程式菜鳥自學C++資料結構演算法 – 堆疊Stack介紹與建立
系列文
程式菜鳥自學C++資料結構演算法30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言