• 数据结构与算法 Chapter 2 线性表


    2.1 线性表定义

    线性表是由长度为n的一组节点组成的有限序列,其中除了首末结点之外,每个结点都有直接的前驱结点和后继结点。

    2.2 线性表的顺序存储结构

    顺序存储结构使用一组连续的存储单元来存储线性表。 其特点有:线性表的逻辑顺序与物理顺序一致、数据元素之间的关系采用物理位置的相邻来表示。其可以随机存取,常用一维数组表示:

    #include <iostream>
    using namespace std;
    
    template<class Elem>
    class Alist
    {
    private:
        int maxSize;
        int listSize;
        int curr;
        Elem * listArray;
    public:
        Alist(int size = 100)
        {
            maxSize = size;
            listSize = 0;
            curr = 0;
            listArray = new Elem[maxSize];
        }
        ~Alist(){ delete [] listArray;}
        void clear() {listSize = curr = 0;}
        void Prev() {if (curr>0) curr--;}
        void Next() {if (curr < listSize-1) curr++;}
        bool setPos(int pos)
        {
            if (pos < 0 || pos >= listSize)
                return false;
            curr = pos;
        }
        bool insert(const Elem it)
        {
            if (listSize == maxSize) return false;
            for (int i = listSize; i > curr; i--)
                listArray[i] = listArray[i-1];
            listArray[curr] = it;
            listSize++; // update length of the list
            return true;
        }
        bool append(const Elem it)
        {
            if (listSize == maxSize) return false;
            listArray[listSize++] = it;
            return true;
        }
        bool remove(Elem & it)
        {
            if (!listSize) return false;
            if (curr < 0 || curr >= listSize) return false;
            it = listArray[curr];
            for (int i = curr; i < listSize-1; i++)
                listArray[i] = listArray[i+1];
            listSize--;
            curr--; // in case you removed the last one
            return true;
        }
        void print()
        {
            for (int i = 0; i < listSize; i++)
                cout << listArray[i] << " ";
            cout << endl;
        }
    };
    
    int main()
    {
        int temp;
        Alist<int> mylist;
        mylist.append(2);
        mylist.append(10);
        mylist.append(8);
        mylist.append(5);
        mylist.append(7);
        mylist.print();
        mylist.setPos(3);
        mylist.insert(100);
        mylist.Next();
        mylist.print();
        mylist.remove(temp);
        mylist.remove(temp);
        mylist.remove(temp);
        mylist.print();
    }
    
    1. 在线性表中随机访问任意位置上的元素,时间复杂度为O(1),因此顺序表示可以随机存取的。
    2. 在顺序存储的线性表中插入或删除一个节点,时间复杂度为O(n),而且需要移动大量元素。
    3. 顺序表空间扩展的代价高,往往需要重新分配。

    2.3 线性表的链式存储结构

    单链表

    单链表的每个数据元素用一个结点来存储,每个结点包含一个数据域和一个指针域头结点是链表最开头的结点,没有存储任何数据元素,通过指针head找到,其他结点的地址通过前驱的next指针找到。最后一个结点没有后继,因此其指针域内容为NULL。下面是单链表的定义、操作实现与一个例子:

    #include <iostream>
    using namespace std;
    
    template<class Elem>
    class Link
    {
    public:
        Elem element;
        Link * next;
        Link(const Elem & item, Link * nextval = NULL) {element = item; next = nextval;}
        Link(Link * nextval = NULL) {next = nextval;}
    };
    
    template<class Elem>
    class LList
    {
    private:
        Link<Elem> * head;
        Link<Elem> * tail;
        Link<Elem> * curr;
        void init() {curr = tail = head = new Link<Elem>;}
        void removeall()
        {
            while (head != NULL)
            {
                curr = head;
                head = head->next;
                delete curr;
            }
        }
    public:
        LList() {init();}
        ~LList() {removeall();}
        bool getValue(Elem & e)
        {
            if (curr == NULL) return false;
            e = curr->element;
            return true;
        }
        Link<Elem> * Locate(Elem e)
        {
            Link<Elem> * p = head;
            while (p != NULL)
            {
                p = p->next;
                if (p->element == e)
                    return p;
            }
            return NULL;
        }
        bool isEmpty() {return head->next == NULL;}
        void Prev()
        {
            if (curr == head) return;
            Link<Elem> * p = head;
            while (p->next != curr)
                p = p->next;
            curr = p;
        }
        void Next()
        {
            if (curr->next == NULL)
                return;
            curr = curr->next;
        }
        bool setPos(int pos)
        {
            Link<Elem> * p = head;
            for (int i = 0; i < pos; i++)
            {
                if (p->next == NULL)
                    return false;
                p = p->next;
            }
            curr = p;
            return true;
        }
        bool Insert(Elem x)
        {
            Link<Elem> * s = new Link<Elem>;
            s->element = x;
            s->next = curr->next;
            curr->next = s;
            return true;
        }
        bool remove(Elem &e)
        {
            if (curr->next == NULL) return false;
            Link<Elem> * q = curr->next;
            e = q->element;
            curr->next = q->next;
            delete q;
            return true;
        }
        void clear() {removeall(); init();}
        void Print()
        {
            Link<Elem> * p = head;
            while (p->next != NULL)
            {
                p = p->next;
                cout << p->element << " ";
            }
            cout << endl;
        }
    };
    
    int main()
    {
        int a;
        LList<int> mylist;
        mylist.Insert(2);
        mylist.Insert(3);
        mylist.Insert(8);
        mylist.Print();
        mylist.Next();
        mylist.Next();
        mylist.Insert(10);
        mylist.Prev();
        mylist.remove(a);
        mylist.Print();
        return 0;
    }
    

    在单链表上,删除和插入一个结点的时间复杂度为O(1),但随机访问任意位置上的结点时间复杂度为O(n)

    顺序表与单链表比较

    1. 空间上:顺序表所有空间都用于存储数据,而单链表一部分空间用于存储指针域。
    2. 访问上:顺序表可以O(1)时间访问任意元素,而单链表随机访问的时间复杂度为O(n)。
    3. 插入删除上:顺序表的平均时间复杂度为O(n),而单链表的时间复杂度为O(1)。
      由于插入删除是更经常的操作,因此单链表在很多方面都优于顺序表。

    双向链表

    一个双向链表中的结点包含两个指针域和一个数据域,其中两个指针域分别为前驱后继,改进了单链表找前驱结点时间复杂度为O(n)的缺点。一个带头结点的双向链表的定义、操作实现与一个例子如下:

    #include <iostream>
    using namespace std;
    
    template<class Elem>
    class DLink
    {
    public:
        Elem element;
        DLink * prev;
        DLink * next;
        DLink(const Elem & it, DLink * p = NULL, DLink * n = NULL)
        {
            element = it;
            prev = p;
            next = n;
        }
        DLink(DLink *p = NULL, DLink *n = NULL) {prev = p; next = n;}
    };
    
    template<class Elem>
    class DList
    {
    private:
        DLink<Elem> * head, * tail, * curr;
    public:
        DList()
        {
            head = tail = curr = new DLink<Elem>;
        }
        ~DList()
        {
            while (head != NULL)
            {
                curr = head;
                head = head->next;
                delete curr;
            }
        }
    
        void Prev() {curr = curr->prev;}
        void Next() {curr = curr->next;}
        bool setPos(int pos)
        {
            DLink<Elem> * p = head;
            for (int i = 0; i < p; i++)
            {
                if (p->next == NULL) return false;
                p = p->next;
            }
            curr = p;
        }
        bool getValue(Elem & x)
        {
            if (head->next == NULL) return false;
            x = curr->element;
            return true;
        }
        bool Insert(const Elem x) // insert an element after curr
        {
            if (curr->next != NULL)
            {
    
                DLink<Elem> * newptr, * nextptr;
                nextptr = curr->next;
                newptr = new DLink<Elem>(x, curr, nextptr);
                curr->next = newptr;
                nextptr->prev = newptr;
                return true;
            }
            else // when curr->next == NULL
            {
                DLink<Elem> * newptr = new DLink<Elem>(x, curr, NULL);
                curr->next = newptr;
                return true;
            }
        }
        bool remove(Elem & x) // remove the element of curr
        {
            if (head->next == NULL || curr == NULL) return false;
            DLink<Elem> * temp = curr;
            x = temp->element;
            curr->prev->next = curr->next;
            curr->next->prev = curr->prev;
            curr = curr->next; // reset curr to a valid node
            delete temp;
            return true;
        }
        void Print()
        {
            DLink<Elem> * p = head;
            while (p->next != NULL)
            {
                p = p->next;
                cout << p->element << " ";
            }
            cout << endl;
        }
    };
    
    int main()
    {
        int a;
        DList<int> mylist;
        mylist.Insert(2);
        mylist.Insert(3);
        mylist.Insert(8);
        mylist.Print();
        mylist.Next();
        mylist.Next();
        mylist.Next();
        //mylist.getValue(a);
        //cout << a << endl;
        mylist.Insert(10);
        mylist.Prev();
        mylist.remove(a);
        mylist.Print();
        return 0;
    }
    

    循环链表

    循环单链表的特点在于,单链表的尾结点rear->next为空,但循环单链表的尾结点rear->next==head,这使得插入删除时不用考虑头尾节点的特殊性。循环双链表类似地有rear->next==headhead->prev==rear。下面是一个循环单链表的定义与操作实现,并用其解决Josephus问题:

    #include <iostream>
    using namespace std;
    
    template<class Elem>
    class CLinkNode
    {
    public:
        Elem element;
        CLinkNode<Elem> * next;
        CLinkNode(CLinkNode<Elem> * nexts = NULL) : next(nexts) {};
        CLinkNode(Elem d, CLinkNode<Elem> * nexts = NULL) : element(d), next(nexts) {}
    };
    
    template<class Elem>
    class CList
    {
    private:
        CLinkNode<Elem> * head, * curr;
    public:
        CList() {head = NULL; curr = NULL;}
        ~CList()
        {
            while (head != NULL)
            {
                curr = head;
                head = head->next;
                delete curr;
            }
        }
        bool Insert(const Elem x)
        {
            if (head == NULL)
            {
                head = new CLinkNode<Elem>(x, head);
                head->next = head;
                curr = head;
                curr = curr->next;
                return true;
            }
            else
            {
                CLinkNode<Elem> * newptr = new CLinkNode<Elem>(x, curr->next);
                newptr->next = curr->next;
                curr->next = newptr;
                curr = curr->next;
                return true;
            }
        }
        void Prev()
        {
            CLinkNode<Elem> * p = curr;
            while (p->next != curr)
                p = p->next;
            curr = p;
        }
        bool remove(Elem & x)
        {
            if (curr == NULL) return false;
            x = curr->element;
            CLinkNode<Elem> * temp = curr;
            Prev();
            curr->next = temp->next;
            delete temp;
            return true;
        }
        CLinkNode<Elem> * getHead() {return head;}
        void Print()
        {
            CLinkNode<Elem> * p = head;
            while (p != head)
            {
                p = p->next;
                cout << p->element << " ";
    
            }
            cout << endl;
        }
    };
    
    int main()
    {
        int n, m;
        cin >> n >> m;
        CList<int> clist;
        for (int i = 1; i <= n; i++)
            clist.Insert(i);
        clist.Print();
        CLinkNode<int> * p = clist.getHead(), * pre = NULL;
        for (int i = 0; i < n; i++)
        {
            for (int j = 1; j < m; j++)
            {
                pre = p;
                p = p->next;
            }
            cout << "OUT: " << p->element << endl;
            m = p->element;
            pre->next = p->next;
            delete p;
            p = pre->next;
        }
        return 0;
    }
    

    2.4 线性表应用

    一元多项式表示

    可以利用单链表表示一元多项式,其中每个结点有两个数据与与一个指针域,两个数据域分别存储多项式当中一项的系数与次数,不同项之间的次数按从头到尾从低到高排列。在此基础上,可以定义多项式链表的加法与减法运算:

    Poly sum(Poly a, Poly b)
    {
        Poly c;
        Node * p = a.getHead()->next;
        Node * q = b.getHead()->next;
        Node * r = c.getHead();
        while (p && q)
        {
            if (p->exp == q->exp)
            {
                Node * s = new Node(p->coef + q->coef, p->exp, NULL);
                r->next = s;
                r = s;
                p = p->next;
                q = q->next;
            }
            else if(p->exp > q->exp)
            {
                Node * s = new Node(q->coef, q->exp, NULL);
                r->next = s;
                r = s;
                q = q->next;
            }
            else if(p->exp < q->exp)
            {
                Node * s = new Node(p->coef, p->exp, NULL);
                r->next = s;
                r = s;
                p = p->next;
            }
        }
        if (p) r->next = p;
        else r->next = q;
        return c;
    }
    

    商品链更新

    与一元多项式的表示相似,也可以由单链表存储并定义加减法以追踪货物情况。

    2.5 本章练习

    Ex13. 设线性表L=(a1,a2,...,an)以带头结点的单链表作存储结构。请编写函数使得当n为奇数时让L成为L=(a2,a4,...,an-1,a1,...,an)

    #include <iostream>
    using namespace std;
    
    template<class Elem>
    class Link
    {
    public:
        Elem element;
        Link * next;
        Link(const Elem & item, Link * nextval = NULL) {element = item; next = nextval;}
        Link(Link * nextval = NULL) {next = nextval;}
    };
    
    template<class Elem>
    class LList
    {
    private:
        Link<Elem> * head;
        Link<Elem> * tail;
        Link<Elem> * curr;
        void init() {curr = tail = head = new Link<Elem>;}
        void removeall()
        {
            while (head != NULL)
            {
                curr = head;
                head = head->next;
                delete curr;
            }
        }
    public:
        LList() {init();}
        ~LList() {removeall();}
        bool getValue(Elem & e)
        {
            if (curr == NULL) return false;
            e = curr->element;
            return true;
        }
        Link<Elem> * Locate(Elem e)
        {
            Link<Elem> * p = head;
            while (p != NULL)
            {
                p = p->next;
                if (p->element == e)
                    return p;
            }
            return NULL;
        }
        bool isEmpty() {return head->next == NULL;}
        void Prev()
        {
            if (curr == head) return;
            Link<Elem> * p = head;
            while (p->next != curr)
                p = p->next;
            curr = p;
        }
        void Next()
        {
            if (curr->next == NULL)
                return;
            curr = curr->next;
        }
        bool setPos(int pos)
        {
            Link<Elem> * p = head;
            for (int i = 0; i < pos; i++)
            {
                if (p->next == NULL)
                    return false;
                p = p->next;
            }
            curr = p;
            return true;
        }
        bool Insert(Elem x)
        {
            Link<Elem> * s = new Link<Elem>;
            s->element = x;
            s->next = curr->next;
            curr->next = s;
            curr = curr->next;
            return true;
        }
        bool remove(Elem &e)
        {
            if (curr->next == NULL) return false;
            Link<Elem> * q = curr->next;
            e = q->element;
            curr->next = q->next;
            delete q;
            return true;
        }
        void clear() {removeall(); init();}
        void Print()
        {
            Link<Elem> * p = head;
            while (p->next != NULL)
            {
                p = p->next;
                cout << p->element << " ";
            }
            cout << endl;
        }
        Link<Elem> * getHead() {return head;}
    };
    
    template<class Elem>
    void Shuffle(LList<Elem> & l)
    {
        Link<Elem> * l2, *p, *q;
        p = l.getHead();
        l2 = new Link<Elem>; // head node
        q = l2;
        int count = 1;
        while (p->next)
        {
            if (count % 2 == 1)
            {
                q->next = p->next;
                q = q->next;
                p->next = p->next->next;
                count++;
            }
            else
            {
                p = p->next;
                count++;
            }
        }
        p->next = l2->next;
    }
    
    int main()
    {
        LList<int> l;
        for (int i = 1; i < 10; i++)
            l.Insert(i);
        l.Print();
        Shuffle(l);
        l.Print();
        return 0;
    }
    

    Ex20. 写一算法将单链表中所有重复的结点删除,使所得结果表中各结点值均不相同。

    #include <iostream>
    using namespace std;
    
    template<class Elem>
    class Link
    {
    public:
        Elem element;
        Link * next;
        Link(const Elem & item, Link * nextval = NULL) {element = item; next = nextval;}
        Link(Link * nextval = NULL) {next = nextval;}
    };
    
    template<class Elem>
    class LList
    {
    private:
        Link<Elem> * head;
        Link<Elem> * tail;
        Link<Elem> * curr;
        void init() {curr = tail = head = new Link<Elem>;}
        void removeall()
        {
            while (head != NULL)
            {
                curr = head;
                head = head->next;
                delete curr;
            }
        }
    public:
        LList() {init();}
        ~LList() {removeall();}
        bool getValue(Elem & e)
        {
            if (curr == NULL) return false;
            e = curr->element;
            return true;
        }
        Link<Elem> * Locate(Elem e)
        {
            Link<Elem> * p = head;
            while (p != NULL)
            {
                p = p->next;
                if (p->element == e)
                    return p;
            }
            return NULL;
        }
        bool isEmpty() {return head->next == NULL;}
        void Prev()
        {
            if (curr == head) return;
            Link<Elem> * p = head;
            while (p->next != curr)
                p = p->next;
            curr = p;
        }
        void Next()
        {
            if (curr->next == NULL)
                return;
            curr = curr->next;
        }
        bool setPos(int pos)
        {
            Link<Elem> * p = head;
            for (int i = 0; i < pos; i++)
            {
                if (p->next == NULL)
                    return false;
                p = p->next;
            }
            curr = p;
            return true;
        }
        bool Insert(Elem x)
        {
            Link<Elem> * s = new Link<Elem>;
            s->element = x;
            s->next = curr->next;
            curr->next = s;
            return true;
        }
        bool remove(Elem &e)
        {
            if (curr->next == NULL) return false;
            Link<Elem> * q = curr->next;
            e = q->element;
            curr->next = q->next;
            delete q;
            return true;
        }
        void clear() {removeall(); init();}
        void Print()
        {
            Link<Elem> * p = head;
            while (p->next != NULL)
            {
                p = p->next;
                cout << p->element << " ";
            }
            cout << endl;
        }
        Link<Elem> * getHead() {return head;}
    };
    
    template<class Elem>
    void noRedun(LList<Elem> & l)
    {
        Link<Elem> * p, * q, * pre;
        p = l.getHead();
        p = p->next;
        q = p->next;
        pre = p;
        while (p->next)
        {
            q = p->next;
            pre = p;
            while (q)
            {
                if (p->element == q->element)
                {
                    Link<Elem> * temp = q;
                    pre->next = q->next;
                    delete q;
                    q = pre->next;
                }
                else
                {
                    pre = q;
                    q = q->next;
                }
            }
            p = p->next;
        }
    }
    
    int main()
    {
        LList<int> l;
        for (int i = 1; i < 10; i++)
            l.Insert(rand() % 10);
        l.Print();
        noRedun(l);
        l.Print();
        return 0;
    }
    
  • 相关阅读:
    win10怎么还原系统【系统天地】
    win7电脑玩游戏不能全屏怎么办【系统天地】
    win10显卡驱动报错怎么办【系统天地】
    win7防火墙在哪里设置【系统天地】
    怎么开启Win10远程桌面连接【系统天地】
    Win7系统C盘空间太小怎么扩容【系统天地】
    vscode 运行创建Vue项目指令出现“因为在此系统上禁止运行脚本“报错解决办法
    关于性能测试的基础——Jmeter
    学习笔记-202008
    EEPROM和flash的区别
  • 原文地址:https://www.cnblogs.com/fsbblogs/p/10466959.html
Copyright © 2020-2023  润新知