静态链表

Stack Link List

  • 静态链表的使用场景
    • 不支持指针的语言
    • 所需要的空间比较固定
  • 两个重要的操作
    • 删除操作
      1. 从头节点开始,找到带删除节点的前一个(前驱)节点。
      2. 这只前驱节点的cur值等于当前待删除节点的cur值以指向当前节点所指向的节点。
      3. 设置被删除节点的状态为 “为未用状态”
    • 插入操作
      1. 找到一个空闲位置代表新插入的节点,在其中存入数据元素。
      2. 从头结点开始,找到带插入位置的前一个(前驱)节点;
      3. 设置新插入节点的cur值以指向前驱节点所指向节点,设置前驱节点的cur值以指向这个新插入节点。
      4. 如果新插入节点是最后一个节点,要设置其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);

 

 
posted @ 2022-07-24 20:59  huahuati  阅读(58)  评论(0)    收藏  举报