数据结构学习2——线性表

1 顺序表

1) 定义顺序表模板类

const int MAXSIZE= 100; //顺序表最大容量
template <class T>
class SeqList
{
    public:
        SeqList() {length = 0;}             //构造函数:建立空顺序表
        SeqList(T d[], int len);            //构造函数:建立长度为len,数据为d[]的顺序表
        ~SeqList(){};                       //析构函数

        int GetLength(){return length};     //表长:获取表长
        T GetValue(int index);              //查找:根据索引获取对应数据
        int GetIndex(T x);                  //查找:根据值获得对应的索引
        void Insert(int index, T x);        //插入:在i位置,插入数据x
        void Delete(int index);             //删除:删除i位置数据
        void PrintList();                   //遍历

    private:
        int length;                         //表长
        T data[MAXSIZE];                    //存放列表元素(数组)
}

2) 成员函数实现

template <class T>
SeqList<T>::SeqList(T d[], int len) //构造函数:建立长度为len,数据为d[]的顺序表
{
    if(len<0 || len>MAXSIZE)
        throw "参数非法";

    length = len;

    for(int i=0;i<len;i++)
        data[i] = d[i];
}

template <class T>
T SeqList<T>::GetValue(int index)       //查找:根据索引获取对应数据
{
    if(index<0 || index>length-1)
        throw "参数非法";
    else
        return data[index];
}

template <class T>
int SeqList<T>::GetIndex(T x)           //查找:根据值获得对应的索引
{
    for(int i=0;i<length;i++)
        if(data[i] == x)
            return i

    return -1;
}

template <class T>
void SeqList<T>::Insert(int index, T x)//插入:在i位置,插入数据x
{
    if(length+1 > MAXSIZE)
        throw "列表已满,无法插入";

    if(index<0 || index>length)
        throw "参数非法";

    for(int i=length-1;i>index-1;i--)
            data[i+1] = data[i];        

    data[index] = x;
    length+=1;
    return;
}

template <class T>
void SeqList<T>::Delete(int index)      //删除:删除i位置数据
{
    if(0 == length)
        throw "列表为空,无法删除";

    if(index<0 || index>length-1)
        throw "参数非法";

    for(int i=index;i<length-1;i++)
            data[i] = data[i+1];

    length--;
}

template <class T>
void SeqList<T>::PrintList()            //遍历
{
    for(int i=0;i<length;i++)
        std::cout<<data[i]<<" ";
}

3) 有序表重复元素删除

template <class T>
void DelDup(SeqList<T> &L)
{
    for(int i=0;i<L.GetLength()-1;i++)
        while(L.GetValue(i)==L.GetValue(i+1))       //删除元素后,后续元素前移,因此依然是比较 i和i+1
            L.Delete(i+1);
}

4) 有序表合并

//不修改原表,假设合成的表有足够的空间容纳两个表的元素
template <class T>
void Merge(const SeqList<T> &L1, const SeqList<T> &L2,SeqList<T> &L3)
{
    int len1 = L1.GetLength();
    int len2 = L2.GetLength();
    int i=j=0;  //两个指针
    int k=0;
    while(i<len1 && j<len2)
    {
        if(L1[i] <= L2[j])
            L3.Insert(k++,L1[i++]);
        else
            L3.Insert(k++,L2[j++]);
    }
    while(i<len1)
        L3.Insert(k++,L1[i++]);

    while(j<len2)
        L3.Insert(k++,L2[j++]);

}

//在原表的基础上,添加,假设原表有足够的空间容纳添加的表
template <class T>
void Merge(SeqList<T> &L1, const SeqList<T> &L2)
{
    int len1 = L1.GetLength();
    int len2 = L2.GetLength();
    int i=len1-1;
    int j=len2-1;
    int k=len1+len2-1;

    while(i>-1 && j>-1)
    {
        if(L1[i]>L2[j])
            L1[k--]=L1[i--];
        else
            L1[k--]=L2[j--];        
    }

    while(j>-1)
        L1[k--]=L2[j--];
}

2 链表

链表与顺序表

1) 空间上,顺序表静态分配内存,结构紧凑,但需要预估内存大小,不够灵活, 另外空间连续性会导致存储碎片
2) 时间上,顺序表按位存取快,两者按值存取均为O(n);顺序表增删数据效率不高,链表的增删时间主要花费在指针移动

单链表

data next

表达:链表由结点构成,以头结点为链表起始,结点包括数据和下一结点的指针,头结点数据为null,尾指针为null(为使得空链表与非空链表中结点表达方式的统一,引入头结点)

1) 定义链表模板类

template <class T>
class LinkList
{
    public:
        LinkList();                         //构造函数:只有头结点的空单链表
        LinkList(T d[], int len);           //构造函数:以数组元素建立链表
        ~LinkList(){};                      //析构函数

        int GetLength();                    //表长
        T GetValue(int index);              //查找:根据索引获取对应数据
        int GetIndex(T x);                  //查找:根据值获得对应的索引
        void Insert(int index, T x);        //插入:在i位置,插入数据x
        void Delete(int index);             //删除:删除i位置数据
        void PrintList();                   //遍历

    private:
        Node<T>* first;     //头指针
}

2) 成员函数实现

表长/查找/插入/删除/构造/析构:遍历即工作指针的后移与非空的判断,在遍历的基础上计数、查找、删除,注意删除和析构中,先备份再delete

template <class T>
LinkList<T>::LinkList()             //构造函数:只有头结点的空单链表
{
    first = new Node<T>;            //生成头结点
    first->next = NULL;             //指向空值
}

template <class T>
LinkList<T>::LinkList(T d[], int len)//构造函数:以数组元素建立链表,这个算法的核心就是不断申请新的节点,申请后,再考虑数据域和指针域,最后考虑首尾的处理
{
    if(len<1)
        throw "参数非法";

    Node<T> *p,*q;
    first = new Node<T>;            //头结点,注意不是首元结点
    p = first;  

    for(int i=0;i<len;i++)
    {
        q= new Node<T>;             //新申请一个结点
        q->data = d[i];             //写入数据
        p->next = q;                //把地址给前一个结点的指针域
        p = q;                      //把当前节点更新成上一个节点
    }   
    p->next = NULL;                 //单链表的尾指针为空
}

template <class T>
LinkList<T>::~LinkList()            //析构函数
{
    Node<T> *q;
    while(first != NULL) 
    {
        q = first;
        first = first->next;
        delete q;
    }
}

template <class T>
void LinkList<T>::PrintList()       //遍历
{
    Node<T> *p = first->next;
    while(p != NULL)
    {
        cout<<p->data;
        p = p->next;
    }
}

template <class T>
int LinkList<T>::GetLength()        //表长
{
    int cnt = 0;
    Node<T> *p = first->next;
    while(p != NULL)
    {
        cnt++;
        p = p->next;
    }
    return cnt;
}

template <class T>
T LinkList<T>::GetValue(int index)  //查找:根据索引获取对应数据
{
    int i = 0;
    Node<T> *p = first->next;
    while(p!=NULL)
    {
        if(i == imdex)
            return p->data;

        i++;
        p = p->next;
    }

    throw "参数非法";
}

template <class T>
int LinkList<T>::GetIndex(T x)      //查找:根据值获得对应的索引
{
    int index = 0;
    Node<T> *p = first->next;
    while(p!=NULL)
    {
        if(p->data == x)
            return index;

        index++;
        p = p->next;
    }

    return -1;
}

template <class T>
void LinkList<T>::Insert(int index, T x)//插入:在i位置,插入数据x
{
    int i = 0;
    Node<T> *p = first->next;
    while(p!=NULL)
    {
        if((i+1) == index)      //指针定位到index结点的前一个结点
            break;

        i++;
        p = p->next;
    }

    if(p==NULL)
        throw "参数非法";

    Node<T> *q = new Node<T>;
    q->data = x;
    q->next = p->next;
    p->next = q;
}

template <class T>
void LinkList<T>::Delete(int index) //删除:删除i位置数据
{
    int i = 0;
    Node<T> *p = first->next;
    while(p!=NULL)
    {
        if((i+1) == index)      //指针定位到index结点的前一个结点
            break;

        i++;
        p = p->next;
    }

    if(p==NULL)
        throw "参数非法";

    Node<T> *q = p->next;   //备份待删除的结点的指针
    p->next = q->next;
    delete q;   
}

3) 单链表原地逆置

//接着在类里定义逆置函数
//另存当前点的后继,然后修改为前驱
template <class T>
void LinkList<T>::Reverse() //原地逆置
{
    Node<T> *pLast, *pCur, *pNext;
    pLast = first;          //上一个结点:初始化为头结点
    pCur = first->next;     //当前结点:初始化为首元结点
    pNext = NULL;

    while(pCur != NULL)
    {
        pNext = pCur->next;     //保存下一个结点的指针
        pCur->next = pLast;     //当前点把前驱作为自己的后继

        pLast = pCur;           //更细当前点和前一个点
        pCur = pNext;
    }
    first->next = pLast;
}   

4) 单链表有序性、排序、合并、删除重复

1) 有序性

template <class T>
int LinkList<T>::IsOrdering() 
{
    Node<T> *p,*q = NULL;
    p = first->next;
    int flag = 1;
    if(p) 
        q = p->next;

    /*比较相邻的结点是否逆序*/
    while((p != NULL) && (q != NULL)) 
    {
        if(p->data > q->data) 
        {
            flag = 0;
            break;
        }
        else
        {
            p = q;
            q = q->next;
        }
    }
    return flag;
}

2) 排序

//保留首元结点,从第二个结点开始依次前插
//每插入一个值,都从first开始遍历当前排好序的链表
template <class T>
void LinkList<T>::Sort() 
{
    Node<T> *p,*q,*r,*s;
    p = first->next;

    if(p != NULL) 
    {
        //在首元结点后断开
        q = p->next;
        p->next = NULL; 

        //依次将q插入到有序单链表中
        while(q)
        {
            r = q->next;    //保存后继
            s = first;      //从first开始遍历
            while((s->next) && ((s->next->data) < (q->data))) //符合顺序则s后移,逆序则退出循环,执行插入操作
                s = s->next;

            q->next = s->next;
            s->next = q;

            //q后移
            q = r; 
        }
    }    
}

3) 有序表合并

template <class T>
void Merge(LinkList<T> &L1, LinkList<T> &L2) 
{
    /*将单链表L1和L2合并至L1*/
    Node<T> *first1, *first2;
    first1 = L1.GetFirst(); /*获取单链表L1的头指针*/
    first2 = L2.GetFirst(); /*获取单链表L2的头指针*/ 
    Node<ElemType> *p,*q,*r;
    r = first1; /*单链表L1的尾指针*/
    p = first1->next;
    q = first2->next;
    while(p != NULL && q != NULL) 
    {
        if(p->data <= q->data) 
        {
            r->next = p;
            r = p;
            p = p->next;
        }
        else 
        {
            r->next = q;
            r = q;
            q = q->next;
        }
    }
    while(p != NULL) 
    {
            r->next = p;
            r = p;
            p = p->next;
    }
    while(q != NULL) 
    {
            r->next = q;
            r = q;
            q = q->next;
    }
    r->next = NULL;
}

4) 删除重复

//两个指针,比较,相等则删除后一个,删除之前要另存下下个结点
template <class T>
void LinkList<T>::LinkListDelDup() 
{
    Node<T> *p,*q,*r;
    p = first->next;
    while(p != NULL) 
    {
        q = p->next;
        while(q != NULL && p->data == q->data) 
        {
            /*删除q*/
            r = q->next;
            p->next = q->next;
            delete q;
            q = r;
        }
        p = p->next;
    }
}

5) 无序表删除重复

//对每个结点,比较后面的所有节点,只向后比较,不向前比较,O(n2)
template <class T>
void LinkList<T>::LinkListDelNormalDup() 
{
    Node<T> *p,*q,*r;
    p = first->next;
    while(p != NULL)
    {
        q = p;
        while(q->next != NULL) 
        {
            if(p->data == q->next->data) 
            {
                r = q->next;                
                q->next = r->next; 
                delete r;
            }
            else
                q = q->next;    
        }
        p = p->next;
    }
}

6) 循环链表:尾标志指向头结点

双向链表

prior data next

1) 插入: 指针修改顺序,未被处理的部分不断开
2) 删除
在这里插入图片描述

系列参考

1) 《数据结构(C++)边学边做》任平红等著
2) 《剑指offer》何海涛著

Leave a Reply

Your email address will not be published. Required fields are marked *