900 likes | 1.17k Views
第三章 链表. 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵. 一、单链表. 线性表的链式表示. 顺序表的优点是可以随机选取表中元素 缺点是插入删除操作复杂。 用指针将互不相连的内存结点串成的 线性表叫 线性链表 。 结点 node 由一个数据元素域,一个或几个 指针域组成。 单链表 的结点只有一个指针域。. 几个结点,前一个结点的指针,指向后一个结点,就连接成一个 线性链表 。. 线性链表 的优点则是插入,删除快捷,缺点是选取复杂。. #include <stdlib.h〉 #include <iostream.h>
E N D
第三章 链表 • 单链表 • 循环链表 • 多项式及其相加 • 双向链表 • 稀疏矩阵
线性表的链式表示 顺序表的优点是可以随机选取表中元素 缺点是插入删除操作复杂。 用指针将互不相连的内存结点串成的 线性表叫线性链表。 结点 node由一个数据元素域,一个或几个 指针域组成。单链表的结点只有一个指针域。
几个结点,前一个结点的指针,指向后一个结点,就连接成一个线性链表。几个结点,前一个结点的指针,指向后一个结点,就连接成一个线性链表。 线性链表的优点则是插入,删除快捷,缺点是选取复杂。
#include <stdlib.h〉 #include <iostream.h> template <class T> class Node { Node<T> *next; //next 是下一个结点的地址 public: T data; // the data is public Node(const T& item, Node<T>* ptrnext=NULL); // list modification methods void InsertAfter(Node<T> *p); Node<T> * DeleteAfter(void); // obtain the address of the next node Node<T> *NextNode(void) const; }; 1. 结点类的定义
// constructor. initialize data and // pointer members template <class T> Node<T>::Node(const T& item, Node<T>* ptrnext) : data(item), next(ptrnext)) { }
// return value of private member next template <class T> Node<T>* Node<T>::NextNode(void) const { return next; }
// insert a node p after the current one template <class T> void Node<T>::InsertAfter(Node<T> *p) { // p points to successor of the current // node, and current node points to p. p->next = next; next = p; }
// delete the node following current and return its address template <class T> Node<T>* Node<T>::DeleteAfter(void) { // save address of node to be deleted Node<T>* tempPtr = next; // if there isn't a successor, return NULL if (next == NULL) return NULL; // current node points to successor of tempPtr. next = tempPtr->next; // return the pointer to the unlinked node return tempPtr; }
2.人工建立一个链表 void main(void) { Node<char>*a,*b,*c; a=new Node<char>('a'); b=new Node<char>('b'); c=new Node<char>('c'); Node<char>*head,*p; head=new Node<char>(' '); p=head; head->InsertAfter(a); head->InsertAfter(b); head->InsertAfter(c); while(p!=NULL) { cout << p->data<<" "; p=p->NextNode( ); } } • 测试结果:打印 c b a
3. 定义线性链表的一些操作 #include "node.h" // allocate a node with data member item and pointer nextPtr template <class T> Node<T>*GetNode(constT&item,Node<T>*nextPtr = NULL) { Node<T> *newNode; // allocate memory while passing item and NextPtr to // constructor. terminate program if allocation fails newNode = new Node<T>(item, nextPtr); if (newNode == NULL) { cerr << "Memory allocation failure!" << endl; exit(1); } return newNode; }
enum AppendNewline {noNewline, addNewline}; template <class T> // print a linked list void PrintList (Node<T> *head, AppendNewline addnl = noNewline ) { // currPtr chains through the list, starting at head Node<T> *currPtr = head; // print the current node's data until end of list while(currPtr != NULL) { // output newline if addl == addNewline if(addnl == addNewline) cout << currPtr->data << endl; else cout << currPtr->data << " "; // move to next node currPtr = currPtr->NextNode( ); } }
// find an item in a linked list head; return TRUE and // value of previous pointer if found; otherwise return FALSE template <class T> int Find(Node<T> *head, T& item, Node<T>* &prevPtr) { Node<T> *currPtr = head; // begin traversal at first node prevPtr = NULL; // cycle through the list until end of list while(currPtr != NULL) { if (currPtr->data == item) {item = currPtr->data; return1;} prevPtr = currPtr; currPtr = currPtr->NextNode( );} return 0; // failed to locate item }
// insert item at the front of list template <class T> void InsertFront(Node<T>* & head, Titem) { // allocate new node so it points to original list head // update the list head head = GetNode(item,head); }
// find rear of the list and append item template <class T> void InsertRear(Node<T>* & head, const T& item) { Node<T> *newNode, *currPtr = head; if (currPtr == NULL) // if list is empty, insert item at the front InsertFront(head,item); else { // find the node whose pointer is NULL while(currPtr->NextNode( ) != NULL) currPtr = currPtr->NextNode( ); // allocate node and insert at rear (after currPtr) newNode = GetNode(item); currPtr->InsertAfter(newNode); } }
// delete the first node of the list template <class T> void DeleteFront(Node<T>* & head) { // save the address of node to be deleted Node<T> *p = head; // make sure list is not empty if (head != NULL) { // move head to second node and delete original head = head->NextNode( ); delete p; } }
// delete the first occurrence of key in the list template <class T> void Delete (Node<T>* & head, T key) { Node<T> *currPtr = head, *prevPtr = NULL; if (currPtr == NULL) return; while (currPtr != NULL && currPtr->data != key) { prevPtr = currPtr; currPtr = currPtr->NextNode( ); } if (currPtr != NULL) { if(prevPtr == NULL) head = head->NextNode(); else prevPtr->DeleteAfter(); delete currPtr; } }
// insert item into the ordered list template <class T> void InsertOrder(Node<T>* & head, Titem) { Node<T> *currPtr, *prevPtr, *newNode; prevPtr = NULL; currPtr = head; while (currPtr != NULL) { if (item < currPtr->data) break; prevPtr = currPtr; currPtr = currPtr->NextNode( ); } if (prevPtr == NULL) InsertFront(head,item); else { newNode = GetNode(item); prevPtr->InsertAfter(newNode); } }
// delete all the nodes in the linked list template <classT> void ClearList(Node<T> * &head) { Node<T> *currPtr, *nextPtr; currPtr = head; while(currPtr != NULL) { nextPtr = currPtr->NextNode( ); delete currPtr; currPtr = nextPtr; } head = NULL; }
链表插入排序 #include <iostream.h> #pragma hdrstop #include "node.h" #include "nodelib.h"
template <class T>void LinkSort(T a[], int n) { Node<T> *ordlist = NULL, *currPtr; int i; for (i=0;i < n;i++)InsertOrder(ordlist, a[i]); currPtr = ordlist; i = 0; while(currPtr != NULL) { a[i++] = currPtr->data; currPtr = currPtr->NextNode( ); } ClearList(ordlist); }
// scan the array and print its elements void PrintArray(int a[], int n) { for(int i=0;i < n;i++) cout << a[i] << " "; }
/*void main(void) { // initialized array with 10 integer values int A[10] = {82,65,74,95,60,28,5,3,33,55}; LinkSort(A,10); // sort the array cout << "Sorted array: "; PrintArray(A,10); // print the array cout << endl; } */ #endif // NODE_LIBRARY
链表的游标类 (Iterator) • 游标类主要用于单链表的搜索。 • 游标类的定义原则: • Iterator类是List类和ListNode类的友元类。 • Iterator对象引用已有的List类对象。 • Iterator类有一数据成员current,记录对单链表最近处理到哪一个结点。 • Iterator类提供若干测试和搜索操作
表示链表的三个类的模板定义 enum Boolean { False, True }; template <class Type> class List; template <class Type> class ListIterator; template <class Type> class ListNode { //表结点 friend class List <Type>; friend class ListIterator <Type>; public: ……… private: Type data; ListNode<Type> *link; };
template <class Type> classList {//链表类 public: ……… private: ListNode<Type>*first, *last; }; template <class Type> class ListIterator { private: constList<Type>& list;//引用已有链表 ListNode<Type> *current;//当前结点指针public: ListNode<Type> *listfirst; ?
ListIterator ( const List<Type>& l ) : list ( l ), current ( l.first){ } //构造函数: 引用链表l, 表头为当前结点 ListNode<Type> * Firster ( ) { current = list.first; return current; } //当前指针置于表头, 返回表头结点地址 Boolean NotNull ( );//检查当前指针空否 Boolean NextNotNull ( ); //检查链表中下一结点是否非空 ListNode <Type> *First ( ); //返回第一个结点的地址 ListNode <Type>*Next ( ); //返回链表当前结点的下一个结点的地址 }
链表的游标类成员函数的实现 template <class Type> Boolean ListIterator<Type> :: NotNull( ) { //检查链表中当前元素是否非空 if ( current != NULL ) return True; else return False; } current current 情况 1 返回True情况 2 返回False
template <class Type> Boolean ListIterator<Type> :: NextNotNull ( ) { //检查链表中下一元素是否非空 if ( current != NULL && current->link != NULL ) return True; else return False; } current current 情况 1 返回True情况 2 返回False
template <class Type> ListNode<Type>* ListIterator<Type> :: First ( ) { //返回链表中第一个结点的地址 if ( list.first->link != NULL ) { current = list.first->link; return current; } else { current = NULL; return NULL; } } 链表中有表头结点 list.first current
template <class Type> ListNode<Type>* ListIterator<Type> :: Next ( ) { //返回链表中当前结点的下一个结点的地址 if ( current != NULL && current->link != NULL ) { current = current->link; return current; } else { current = NULL; return NULL; } } current current
利用游标类 (iterator) 计算元素的和 int sum ( const List<int>&L ) { ListIterator<int> li (L); //定义游标对象, current 指向li.first if (li. First( ) ==NULL) return 0; //链表为空时返回0 int retval =current->data;//第一个元素 while ( li.nextNotNull ( ) )//链表未扫描完 retval += li.Next( )->data;//累加 return retval; }
静态链表定义 const int MaxSize = 100;//静态链表大小 template <class Type> class StaticList; template <class Type> class SListNode { friend class StaticList<Type>; Type data;//结点数据 int link;//结点链接指针 } template <class Type> class StaticList { SListNode<Type> Nodes[MaxSize]; int newptr; //当前可分配空间首地址 }
将链表空间初始化 template <class Type> void StaticList<Type> :: InitList ( ) { Nodes[0].link = -1; newptr = 1; //当前可分配空间从 1 开始建 //立带表头结点的空链表 for ( int i = 1; i < MaxSize-1; i++ ) Nodes[i].link = i+1;//构成空闲链接表 Nodes[MaxSize-1].link = -1; //链表收尾 }
在静态链表中查找具有给定值的结点 template <class Type> int StaticList<Type> :: Find ( Type x ) { int p = Nodes[0].link; //指针 p 指向链表第一个结点 while ( p != -1 ) if ( Nodes[p].data != x) p = Nodes[p].link; else break; //逐个结点检测查找具有给定值的结点 return p; }
在静态链表的表尾追加一个新结点 template <class Type> int StaticList<Type> :: Append ( Type x ) { if ( newptr == -1 ) return 0; //追加失败 int q = newptr; //分配结点 newptr = Nodes[newptr].link; Nodes[q].data = x; Nodes[q].link = -1; int p = 0; //查找表尾 while ( Nodes[p].link != -1 ) p = Nodes[p].link; Nodes[p].link = q; //追加 return 1; }
在静态链表中查找第i个结点 template <class Type> int StaticList<Type> :: Locate ( int i ) { if ( i < 0 ) return-1; //参数不合理 if ( i == 0 ) return 0; int j = 0, p = Nodes[0].link; while ( p != -1 && j < i ) { p = Nodes[p].link; j++; } //循链查找第 i号结点 return p; }
在静态链表第 i个结点处插入一个新结点 template <class Type> int StaticList<Type> :: Insert ( int i, Type x ) { int p = Locate ( i-1 ); if ( p == -1 ) return 0; //找不到结点 int q = newptr; //分配结点 newptr = Nodes[newptr].link; Nodes[q].data = x; Nodes[q].link = Nodes[p].link; //链入 Nodes[p].link = q; return 1; }
在静态链表中释放第 i 个结点 template <class Type> int StaticList<Type> :: Remove ( int i ) { int p = Locate ( i-1 ); if ( p == -1 ) return 0; //找不到结点 int q = Nodes[p].link; //第 i 号结点 Nodes[p].link = Nodes[q].link; Nodes[q].link = newptr; //释放 newptr = q; return 1; }
二、循环链表 (Circular List) • 循环链表是单链表的变形。 • 循环链表的最后一个结点的link指针不为 NULL,而是指向了表的前端。 • 为简化操作,在循环链表中往往加入表头结点。 • 循环链表的特点是:只要知道表中某一结点的地址,就可搜寻到所有其他结点的地址。
first a0 a1 a2 an-1 • 循环链表的示例 • 带表头结点的循环链表 first a0 a1 an-1 (非空表) first (空表)
循环链表类的定义 template <class Type> class CircList; template <class Type> class CircListNode { friend class CircList <Type>; public: CircListNode ( Type d = 0, CircListNode<Type> *next = NULL ) : data ( d ), link ( next ) { } //构造函数private: Type data; //结点数据 CircListNode<Type> *link; //链接指针 }
template <class Type> class CircList { private: CircListNode<Type> *first, *current, *last; //链表的表头指针、当前指针和表尾指针 public: CircList ( Type value ); ~CircList ( ); int Length ( ) const; Boolean IsEmpty ( ) { return first->link == first;} Boolean Find ( const Type & value );
Type getData( ) const; void Firster ( ) { current = first; } Boolean First ( ); Boolean Next ( ); Boolean Prior ( ); void Insert ( const Type & value ); void Remove ( ); };
循环链表的搜索算法 31 48 15 57 first 搜索成功 搜索15 current current current first 31 48 15 57 current current current current current 搜索不成功 搜索25
循环链表的搜索算法 template <class Type> CircListNode<Type> * CircList<Type> :: Find ( Type value) { //在链表中从头搜索其数据值为value的结点 current = first->link; //检测指针current 指示第一个结点 while ( current != first && current->data != value ) current = current->link; return current; }
用循环链表求解约瑟夫问题 • 约瑟夫问题的提法 n个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数, 报到第m个人,令其出列。然后再从下一 个人开始,从 1 顺时针报数,报到第m 个人,再令其出列,…,如此下去, 直到圆圈中只剩一个人为止。此人即为优胜者。
约瑟夫问题的解法 #include <iostream.h> #include “CircList.h” Template<Type> void CircList<Type> :: Josephus ( int n, int m ) { First ( ); for ( int i = 0; i < n-1; i++ ) {//执行n-1次 for ( int j = 0; j < m-1; j++ ) Next ( ); cout << “出列的人是” << getData ( ) << endl;//数m-1个人 Remove ( );//删去 }