單(雙向)連結串列的基本操作 C++
阿新 • • 發佈:2019-01-04
#include"iostream" using namespace std; //====================單鏈表=================================== //如果不提供“頭指標”,則整個連結串列都無法訪問,連結串列的一個重要特點是插入、刪除操作靈活方便. //插入操作處理順序:中間節點的邏輯,後節點邏輯,前節點邏輯。按照這個順序處理可以完成任何連結串列的插入操作。 //刪除操作的處理順序:前節點邏輯,後節點邏輯,中間節點邏輯。 /*單鏈表實際上是由節點(Node)組成的,一個連結串列擁有不定數量的節點。 而向外暴露的只有一個頭節點(Head),我們對連結串列的所有操作,都是通過其頭節點(head)來進行的。 節點:資料+地址*/ struct ListNode { int val; struct ListNode *next; ListNode(int x) :val(x), next(NULL){} }; //頭指標與頭結點不同,頭結點即第一個結點,頭指標是指向第一個結點的指標。連結串列中可以沒有頭結點,但不能沒有頭指標。 /*1.初始化單鏈表(無頭結點),頭指標為空*/ // void initList(Node **pNode) // { // *pNode = NULL; // cout << "初始化成功!" << endl; // } /*2.建立單鏈表(頭插法):生成的連結串列中結點的次序和輸入的順序相反 a.先讓新節點的next指向頭節點之後 b.然後讓表頭的next指向新節點*/ void CreatListHead(ListNode *pHead,int data) { ListNode *p=new ListNode(-1); p->val = data; p->next = pHead->next; pHead->next = p; } /*2.建立單鏈表(尾插法):在表的最後插入結點 a.將表尾終端結點的指標指向新結點 b.將當前的新結點定義為表尾終端結點 */ void CreatListTail(ListNode **Node, int data) { if ((*Node)->next == NULL) { ListNode *p = new ListNode(-1); p->val = data; (*Node)->next = p; *Node = p; (*Node)->next = NULL; } } /*單鏈表的遍歷,並返回單鏈表長度*/ int printList(const ListNode *pHead) { int len = 0; ListNode *p = pHead->next; if (p== NULL) cout << "連結串列為空!" << endl; else { while (p != NULL) { len++; cout << p->val << " "; p = p->next; } cout << endl; } return len; } /*刪除單鏈表: 1.宣告一節點p和q; 2.將第一個結點賦值給p; 3.迴圈: a.將下一結點賦值給q;b.釋放p;c.將q賦值給p; */ bool ClearList(ListNode *pHead) { ListNode *p, *q; p = pHead->next; while (p) { q = p->next; delete p; p = q; } pHead->next = NULL; return true; } /*查詢單鏈表上指定節點的資料*/ bool GetElem(ListNode *pHead, int i, int &data) { ListNode *p = pHead->next; int j = 0; while (p && j < i-1) { p = p->next; j++; } if (!p) return false; else { data = p->val; return true; } } /*單鏈表指定位置插入資料: 1.找到ai-1儲存位置p 2.生成一個數據域為x的新結點*s 3.令結點*p的指標域指向新結點 4.新結點的指標域指向結點ai。*/ bool ListInsert(ListNode *pHead, int i, int data) { ListNode *p = pHead; int j = 0; while (p && j < i - 1) { p = p->next; j++; } if (!p || j>i - 1) return false; else { ListNode *tmpPtr = new ListNode(-1); tmpPtr->val = data; tmpPtr->next = p->next; p->next = tmpPtr; return true; } } /*刪除單鏈表指定的某個結點: 1.找到ai-1的儲存位置p(因為在單鏈表中結點ai的儲存地址是在其直接前趨結點ai-1的指標域next中) 2.令p->next指向ai的直接後繼結點(即把ai從鏈上摘下) 3.釋放結點ai的空間,將其歸還給“儲存池”。*/ bool ListDelete(ListNode *pHead, int i) { int j=1; ListNode *p, *q; p= pHead; while (p && j<i) { p = p->next; j++; } if (!p || j>i) return false; else { q = p->next; p->next = q->next; delete q; return true; } } /*單鏈表逆序:*/ bool ListReverse(ListNode *PHead) { ListNode *current, *pnext, *prev; current = PHead->next; pnext = current->next; current->next = NULL; while (pnext) { prev = pnext->next; pnext->next = current; current = pnext; pnext = prev; } PHead->next = current; return true; } /*判斷單鏈表中是否有環: 1.使用p、q兩個指標,p總是向前走,但q每次都從頭開始走,對於每個節點,看p走的步數是否和q一樣*/ bool HasLoop(ListNode *pHead) { ListNode *cur1 = pHead; int pos1 = 0; while (cur1) { ListNode *cur2 = pHead; int pos2 = 0; pos1++; while (cur2) { pos2++; if (cur1 == cur2) { if (pos1 == pos2) break; else return true; } cur2 = cur2->next; } cur1 = cur1->next; } return false; } /*獲取單鏈表倒數第N個結點值: 建立兩個指標,第一個先走n步,然後第2個指標也開始走,兩個指標步伐(前進速度)一致。當第一個結點走到連結串列末尾時,第二個節點的位置就是我們需要的倒數第n個節點的值。*/ bool GetNthNodeFromBack(ListNode *pHead, int n,int &data) { int i = 1; ListNode *firstNode = pHead, *secNode = pHead; while (firstNode->next != NULL && i < n) { firstNode = firstNode->next; i++; } if (firstNode->next == NULL && i < n - 1) { cout << "n超出連結串列長度" << endl; return false; } while (firstNode->next != NULL) { secNode = secNode->next; firstNode = firstNode->next; } data = secNode->val; return true; } //===================================================== //====================雙向迴圈連結串列======================= /*雙向迴圈連結串列的結構:資料、next指標、prior指標 1.連結串列由頭指標head惟一確定的。 2.帶頭結點的雙鏈表的某些運算變得方便。 3.將頭結點和尾結點連結起來,為雙(向)迴圈連結串列。*/ struct NodeType2 { int val; NodeType2 *next, *prior; NodeType2(int x) :val(x), next(NULL), prior(NULL) {}; }; //建立雙向連結串列 void CreateListtype2(NodeType2 **Node, int data) { NodeType2 *tmpPtr = (*Node)->next; NodeType2 *p = new NodeType2(data); (*Node)->next = p; p->prior = *Node; p->next = tmpPtr; *Node = p; } /*雙鏈表的前插操作*/ bool ListType2Insert1(NodeType2 *pHead, int i, int data) { int j = 0; NodeType2 *p = pHead; while (p && j<i) { p = p->next; j++; } if (!p && j < i - 1) return false; else { NodeType2 *tmpTpr = new NodeType2(data); tmpTpr->next = p; tmpTpr->prior = p->prior; p->prior->next = tmpTpr; p->prior = tmpTpr; return true; } } /*雙鏈表的後插操作*/ bool ListType2Insert2(NodeType2 *pHead, int i, int data) { int j = 0; NodeType2 *p = pHead; while (p && j<i) { p = p->next; j++; } if (!p && j < i - 1) { cout << "超出連結串列長度!" << endl; return false; } else { NodeType2 *tmpPtr = new NodeType2(data); tmpPtr->next = p->next; p->next->prior = tmpPtr; p->next = tmpPtr; tmpPtr->prior = p; return true; } } /*雙鏈表上刪除結點*p*/ bool ListType2Delete(NodeType2 *pHead, int i) { int j = 0; NodeType2 *p = pHead; while (p && j<i) { p = p->next; j++; } if (p && j < i - 1) { cout << "超出連結串列長度!" << endl; return false; } else { p->prior->next = p->next; p->next->prior = p->prior; delete p; return true; } } //============================================= int main() { //=========單鏈表============== //ListNode *L = new ListNode(-1); //int n; //ListNode* tmpPtr = L; //while (cin >> n)//ctrl+z結束 //{ // //CreatListHead(L, n); // CreatListTail(&tmpPtr, n);//為什麼傳指標不行 非得指標的指標 //} //int len = printList(L); //int data = 0; // bool isGetElem = GetElem(L, 2, data); // bool isInsertElem = ListInsert(L, 2, 4); // bool isClear = ClearList(L); //bool isDelete = ListDelete(L, 1); //bool isReverse = ListReverse(L); //tmpPtr->next = L; //bool isloop=HasLoop(L); //bool IsGet = GetNthNodeFromBack(L, 2, data); //len = printList(L); //============雙鏈表================== NodeType2 *L = new NodeType2(-1); int n = 0; NodeType2 *tmpPtr = L; while (cin >> n) { CreateListtype2(&tmpPtr, n); } bool isInsert = ListType2Insert2(L, 2, 4); bool isDelete = ListType2Delete(L, 3); system("pause"); return 0; }