静态链表
Stack Link List
- 静态链表的使用场景
- 不支持指针的语言
- 所需要的空间比较固定
- 两个重要的操作
- 删除操作
-
从头节点开始,找到带删除节点的前一个(前驱)节点。
-
这只前驱节点的cur值等于当前待删除节点的cur值以指向当前节点所指向的节点。
-
设置被删除节点的状态为 “为未用状态”
-
- 插入操作
-
找到一个空闲位置代表新插入的节点,在其中存入数据元素。
-
从头结点开始,找到带插入位置的前一个(前驱)节点;
-
设置新插入节点的cur值以指向前驱节点所指向节点,设置前驱节点的cur值以指向这个新插入节点。
-
如果新插入节点是最后一个节点,要设置其cur标记为 “末尾
-
- 删除操作
节点的定义
template<typename T> struct Node { T data; //数据域 int cur; //游标,记录下个静态链表节点的数组下标 };
静态链表的定义
template<typename T> class StaticLinkList { public: StaticLinkList(); ~StaticLinkList(){} public: int findAnIdlePos(); //找到一个空闲位置用于保存数据 bool ListInsert(int i, const T& e); //在第 i 个位置插入指定元素 bool ListDelete(int i); //删除第 i 个位置的元素 bool GetElem(int i, T& e); //获得第 i 个位置的元素值 int LocateElem(const T& e); //按元素值查找其在静态链表中第一次出现的位置 void DispList(); //输出静态链表中的所有元素 int ListLength(); //获取静态链表的长度 bool Empty(); //判断静态链表是否为空 private: Node<T> m_data[MAXSIZE]; //保存节点数据的数组 int m_length; //当前长度,也就是当前保存的数据节点数目 };
插入操作
template<typename T> bool StaticLinkList<T>::ListInsert(int iPos, const T& e) { if(iPos < 1 || iPos > (m_length + 1)) { cout << "元素 " << e << " 插入的位置 " << iPos << " 不不合法,合法的位置" << m_length + 1 << " 之间" <<endl; return false; } int iIdx; if((iIdx = findAnIdlePos()) == -1) //静态链表满了 { cout << "静态链表已满 " <<endl; return false; } //既然需要在第iPos个位置插入元素,那么肯定要找到iPos - 1个位置 int iDataCount = 1; //统计静态链表中元素数量 int iIdxPrev; //保存第 iPos - 1 个位置对应的m_data数组的下标 if(iPos == 1) //向第一个位置插入元素,要单独处理 { m_data[iIdx].data = e; if(m_length == 0) //空表 { m_data[iIdx].cur = e_LAST; } else //非空表 { m_data[iIdx].cur = m_data[0].cur; } m_data[0].cur = iIdx; } else { int iPosCount = 0; //位置计数 int tmpcur = m_data[0].cur; //前面已经判断过位置合法,所以一定可以找到合适的位置,while(true)循环肯定可以正常退出 while(true) { iPosCount++; if(iPosCount >= (iPos - 1)) { iIdxPrev = tmpcur; break; } tmpcur = m_data[tmpcur].cur; } //end while int iTmpCurr = m_data[iIdxPrev].cur; m_data[iIdxPrev].cur = iIdx; m_data[iIdx].data = e; m_data[iIdx].cur = iTmpCurr; } cout << "成功在位置为 " << iPos << " 处插入元素 " << e << endl; m_length++; return true; }
删除操作
template<typename T> bool StaticLinkList<T>::ListDelete(int iPos) { if(m_length < 1) { //今天链表为空 cout << "静态链表为空,不能删除任何数据" <<endl; return false; } if( iPos < 1 || iPos > m_length) { cout << "删除元素的位置 " << iPos << " 不合法,合法的位置是1到 " << m_length << " 之间" <<endl; return false; } int tmpcur = m_data[0].cur; //第一个数据节点的数组下标 if(iPos == 1) //删除第一个位置元素,要单独处理 { if(m_length != 1) { //这个静态链表里有多个元素 m_data[0].cur = m_data[tmpcur].cur; // 头结点指向第二个数据节点的数据下标 } m_data[tmpcur].cur = e_NOUSE; cout << "成功删除位置为 " << iPos << " 的元素, 该元素的值为 " << m_data[tmpcur].data <<endl; } else { int iIdxPrev; //第ipos - 1个位置对应的m_data数据的下标 int iPosCount = 0; //位置计数 //前面已经判断过删除位置合法,所以一定可以找到合适位置,while(true)循环肯定可以正常退出 while(true) { iPosCount++; if(iPosCount >= (iPos - 1)) //找到了 i - 1 个位置 { iIdxPrev = tmpcur; break; } tmpcur = m_data[tmpcur].cur; }//end while int iTmpCurr = m_data[iIdxPrev].cur; //当前要删除的这个节点的数字下标 m_data[iIdxPrev].cur = m_data[iTmpCurr].cur; //前一个节点的cur指向当前要删除节点的cur m_data[iTmpCurr].cur = e_NOUSE; //标记被删除数据节点的数组下标为未用状态 cout << "成功删除位置为 " << iPos << " 的元素, 该元素的值为 " << m_data[iTmpCurr].data <<endl; } m_length--; //实际表长减一 return true; }
整体代码
#include<iostream> using namespace std; #define MAXSIZE 201 //静态链表的尺寸,可以根据时机需要设定该值,可以用数组小标为0 - 200 enum NODEUSE //节点使用情况枚举标记值 { //这些枚举值都是负值,以免和数组下标(从0开始的正值) 冲突 e_NOUSE = -1, //未使用(未用) e_LAST = -2 //最后一个节点(末尾) }; //静态链表中每个节点的定义 template<typename T> struct Node { T data; //数据域 int cur; //游标,记录下个静态链表节点的数组下标 }; //静态链表的定义 template<typename T> class StaticLinkList { public: StaticLinkList(); ~StaticLinkList(){} public: int findAnIdlePos(); //找到一个空闲位置用于保存数据 bool ListInsert(int i, const T& e); //在第 i 个位置插入指定元素 bool ListDelete(int i); //删除第 i 个位置的元素 bool GetElem(int i, T& e); //获得第 i 个位置的元素值 int LocateElem(const T& e); //按元素值查找其在静态链表中第一次出现的位置 void DispList(); //输出静态链表中的所有元素 int ListLength(); //获取静态链表的长度 bool Empty(); //判断静态链表是否为空 private: Node<T> m_data[MAXSIZE]; //保存节点数据的数组 int m_length; //当前长度,也就是当前保存的数据节点数目 }; //通过构造函数对静态链表进行初始化 template<typename T> StaticLinkList<T>::StaticLinkList() { //从下标1开始的节点用于保存实际的数据,这些节点的cur有必要要设置值,而头结点不用设置值 for(int i = 1; i < MAXSIZE; ++i) { m_data[i].cur = e_NOUSE; //标记这些节点都没有使用 } m_length = 0; //还未向其中存入任何数据元素 } //在m_data中找到一个空闲位置用于保存数据,若没有找到(静态链表满了),则返回1 template<typename T> int StaticLinkList<T>::findAnIdlePos() { for(int i = 1; i < MAXSIZE; ++i) //因为下标0是头结点,不能用于保存数据,所以循环变量从1开始 { if(m_data[i].cur == e_NOUSE) return i; } return -1; } //在第iPos个位置(位置编号从1开始)插入指定元素 template<typename T> bool StaticLinkList<T>::ListInsert(int iPos, const T& e) { if(iPos < 1 || iPos > (m_length + 1)) { cout << "元素 " << e << " 插入的位置 " << iPos << " 不不合法,合法的位置" << m_length + 1 << " 之间" <<endl; return false; } int iIdx; if((iIdx = findAnIdlePos()) == -1) //静态链表满了 { cout << "静态链表已满 " <<endl; return false; } //既然需要在第iPos个位置插入元素,那么肯定要找到iPos - 1个位置 int iDataCount = 1; //统计静态链表中元素数量 int iIdxPrev; //保存第 iPos - 1 个位置对应的m_data数组的下标 if(iPos == 1) //向第一个位置插入元素,要单独处理 { m_data[iIdx].data = e; if(m_length == 0) //空表 { m_data[iIdx].cur = e_LAST; } else //非空表 { m_data[iIdx].cur = m_data[0].cur; } m_data[0].cur = iIdx; } else { int iPosCount = 0; //位置计数 int tmpcur = m_data[0].cur; //前面已经判断过位置合法,所以一定可以找到合适的位置,while(true)循环肯定可以正常退出 while(true) { iPosCount++; if(iPosCount >= (iPos - 1)) { iIdxPrev = tmpcur; break; } tmpcur = m_data[tmpcur].cur; } //end while int iTmpCurr = m_data[iIdxPrev].cur; m_data[iIdxPrev].cur = iIdx; m_data[iIdx].data = e; m_data[iIdx].cur = iTmpCurr; } cout << "成功在位置为 " << iPos << " 处插入元素 " << e << endl; m_length++; return true; } //输出静态链表中所有元素,时间复杂度o(n) template<typename T> void StaticLinkList<T>::DispList() { if(m_length < 1) //静态链表为空 { return ; } int tmpcur = m_data[0].cur; while(true) { cout << m_data[tmpcur].data << " "; if( (tmpcur = m_data[tmpcur].cur) == e_LAST) break; } cout << endl;; } //获取第 i 个位置元素值时间复杂度o(n) template<typename T> bool StaticLinkList<T>::GetElem(int i , T& e) { if(m_length < 1) { cout << "静态链表为空,不能获取任何数据" << endl; return false; } if( i < 1 || i > m_length) { cout << "获取元素的位置 " << i << " 不合法,合法的位置是1到 " << m_length << " 之间" <<endl; return false; } int tmpcur = m_data[0].cur; int iPos = 0; while(true) { iPos++; if(iPos == i) { e = m_data[tmpcur].data; cout << "成功获取位置为 " << i << " 的元素,该元素的值为" << e << endl; return true; } tmpcur = m_data[tmpcur].cur; } return false; } //按元素值查找其在静态链表中第一次出现的位置上,时间复杂度o(n) template<typename T> int StaticLinkList<T>::LocateElem(const T& e) { if(m_length < 1) { //今天链表为空 cout << "静态链表为空,不能获取任何数据" <<endl; return -1; } int tmpcur = m_data[0].cur; int iPos = 0; while(true) { iPos++; if(m_data[tmpcur].data == e && m_data[tmpcur].cur != e_NOUSE) { cout << "值为 " << e << " 的元素在静态链表中第一次出现的位置为" << iPos << endl; return tmpcur; } if(m_data[tmpcur].cur == e_LAST) { break; } tmpcur = m_data[tmpcur].cur; } cout << "值为 " << e << " 的元素在静态链表中没有找到" << endl; return -1; } //获取静态链表的长度,时间复杂度o(1) template<typename T> int StaticLinkList<T>::ListLength() { return m_length; } //判断静态链表是否为空 template<typename T> bool StaticLinkList<T>::Empty() { if(m_length < 1) { return true; } return false; } //删除第iPos个位置的元素 template<typename T> bool StaticLinkList<T>::ListDelete(int iPos) { if(m_length < 1) { //今天链表为空 cout << "静态链表为空,不能删除任何数据" <<endl; return false; } if( iPos < 1 || iPos > m_length) { cout << "删除元素的位置 " << iPos << " 不合法,合法的位置是1到 " << m_length << " 之间" <<endl; return false; } int tmpcur = m_data[0].cur; //第一个数据节点的数组下标 if(iPos == 1) //删除第一个位置元素,要单独处理 { if(m_length != 1) { //这个静态链表里有多个元素 m_data[0].cur = m_data[tmpcur].cur; // 头结点指向第二个数据节点的数据下标 } m_data[tmpcur].cur = e_NOUSE; cout << "成功删除位置为 " << iPos << " 的元素, 该元素的值为 " << m_data[tmpcur].data <<endl; } else { int iIdxPrev; //第ipos - 1个位置对应的m_data数据的下标 int iPosCount = 0; //位置计数 //前面已经判断过删除位置合法,所以一定可以找到合适位置,while(true)循环肯定可以正常退出 while(true) { iPosCount++; if(iPosCount >= (iPos - 1)) //找到了 i - 1 个位置 { iIdxPrev = tmpcur; break; } tmpcur = m_data[tmpcur].cur; }//end while int iTmpCurr = m_data[iIdxPrev].cur; //当前要删除的这个节点的数字下标 m_data[iIdxPrev].cur = m_data[iTmpCurr].cur; //前一个节点的cur指向当前要删除节点的cur m_data[iTmpCurr].cur = e_NOUSE; //标记被删除数据节点的数组下标为未用状态 cout << "成功删除位置为 " << iPos << " 的元素, 该元素的值为 " << m_data[iTmpCurr].data <<endl; } m_length--; //实际表长减一 return true; } int main() { StaticLinkList<int> slinkobj; slinkobj.ListInsert(1, 12); slinkobj.ListInsert(1, 24); slinkobj.ListInsert(3, 48); slinkobj.ListInsert(2, 100); slinkobj.ListInsert(5, 190); slinkobj.ListInsert(4, 300); slinkobj.DispList(); slinkobj.LocateElem(190); slinkobj.LocateElem(24); slinkobj.LocateElem(300); cout << "----------------------" <<endl; int temp; slinkobj.GetElem(0, temp); //如果GetElem()返回true,则temp种保存着获取到的元素值 slinkobj.GetElem(1, temp); slinkobj.GetElem(3, temp); slinkobj.GetElem(6, temp); cout << "----------------------" <<endl; slinkobj.DispList(); slinkobj.ListDelete(1); slinkobj.ListDelete(5); slinkobj.ListDelete(10); slinkobj.DispList(); return 0; }
总结
//静态链表的使用总结 //不同的静态链表实现方式 -- 将静态链表中的第一个和最后一个节点作为特殊节点来使用(不保存数据) //第一个节点的cur存放一个未被使用的节点所对应的数组下标 //最后一个节点的cur存放第一个有数据节点对应的数组下标(相当于头结点),该值为0相当于链表为空。 //实现代码繁琐,但插入数据是能够明显提高寻找空闲节点的效率,时间复杂度从o(n)变为o(1);
欢迎大家指出博文中的错误哦。

浙公网安备 33010602011771号