数据结构与算法 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;
}

原文地址:https://www.cnblogs.com/fsbblogs/p/10466959.html

时间: 2024-11-16 18:42:27

数据结构与算法 Chapter 2 线性表的相关文章

Java数据结构和算法之哈希表

五.哈希表 一般的线性表.树中,记录在结构中的相对位置是随机的即和记录的关键字之间不存在确定的关系,在结构中查找记录时需进行一系列和关键字的比较.这一类查找方法建立在"比较"的基础上,查找的效率与比较次数密切相关.理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之间建立一确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应.因而查找时,只需根据这个对应关系f找到给定值K的像f(K).若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上,由此不需

算法实例_线性表 By:比方

算法实例_线性表 By:比方 什么是线性表? 从线性表的功能逻辑上来看,线性表就是由n(n>=0)个数据元素的排序组合,数据由x1,x2,x3,...,xn结构有序的顺序排列. 线性表的结构和特点 1.              仅有一个开始节点x1,没有直接前趋节点,有妾只有一个直接后续节点x2: 2.              仅有一个终结节点xn,仅有一个前趋节点xn-1; 3.              对于同一个线性表,其中没一个数据的元素,都必须具备相同的数据结构类型, 且没一个元素

小猪的数据结构辅助教程——2.2 线性表中的单链表

小猪的数据结构辅助教程--2.2 线性表中的单链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.理解顺序表以及单链表各自的有点以及缺点! 2.熟悉单链表的形式,对于头指针,头结点,尾结点,数据域和指针域这些名词要知道是什么! 3.熟悉单链表的结点结构 4.区分头指针与头结点! 5.熟悉创建单链表的两种方式:头插法和尾插法 6.了解单链表12个基本操作的逻辑 7.有趣的算法题:查找单链表的中间结点~ 1.单链表的引入(顺序表与单链表的PK) 2.单链表的结构图以及一些名

【学习总结】《大话数据结构》- 第3章-线性表

[学习总结]<大话数据结构>- 总 启示: 线性表:零个或多个数据元素的有限序列. 目录 3.1 开场白 3.2 线性表的定义 3.3 线性表的抽象数据类型 3.4 线性表的顺序存储结构 3.5 顺序存储结构的插入与删除 3.6 线性表的链式存储结构 3.7 单链表的读取 3.8 单链表的插入与删除 3.9 单链表的整表创建 3.10 单链表的整表删除 3.11 单链表结构与顺序存储结构优缺点 3.12 静态链表 3.13 循环链表 3.14 双向链表 3.15 总结回顾 3.16 结尾语 -

小猪的数据结构辅助教程——2.7 线性表中的双向循环链表

小猪的数据结构辅助教程--2.7 线性表中的双向循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解引入双向循环链表的原因 2.熟悉双向循环链表的特点以及存储结构 3.掌握双向循环链表的一些基本操作的实现逻辑 4.掌握逆序输出双向循环链表元素逻辑 1.双向循环链表的引入 2.双向循环链表的存储结构 双向循环链表的特点: 上面也说了,空间换时间,比起循环链表只是多了一个指向前驱的指针 特点的话: 判断空表:L ->next = L -> prior = L; 存

小猪的数据结构辅助教程——2.4 线性表中的循环链表

小猪的数据结构辅助教程--2.4 线性表中的循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解单链表存在怎样的缺点,暴露出来的问题 2.知道什么是循环单链表,掌握单链表的特点以及存储结构 3.掌握循环链表的一些基本操作的实现逻辑,最好能手撕代码 1.循环单链表的引入 2.循环链表的特点以及存储结构 循环链表的特点: 上面也说了,比单链表稍微高比格一点的地方就是: 链表最后一个结点的指针域指向了头结点而已,这样形成所谓的环,就是循环单链表了,呵呵! 特点的话有:

小猪的数据结构辅助教程——2.1 线性表中的顺序表

小猪的数据结构辅助教程--2.1 线性表中的顺序表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.抽象数据类型(ADT)的概念,三要素:数据,数据元素间的关系和数据的操作 2.线性表的特点:按照一条线排列的数据集合,1对1,除了首元和尾元,其他元素都有直接前驱和直接后继 3.牢记线性表的存储结构,要理解并熟悉12个基本操作的逻辑,最好能徒手撕出代码 4.求并集,顺序表的经典例子,必须掌握! 1.抽象的数据类型 简单点说: 抽象:有点像我们面向对象语言中的类的思想,将事物

java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制)

转载请注明出处(请尊重原创!谢谢~): http://blog.csdn.net/javazejian/article/details/53073995 出自[zejian的博客] 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) ??这篇是数据结构与算法的第3篇,通过前两篇的介绍,对应顺序表和链表已有

[数据结构 - 第3章补充] 线性表之双向链表(C语言实现)

一.什么是循环链表? 双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域.所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱. 既然单链表也可以有循环链表,那么双向链表当然也可以是循环表. 线性表的双向链表存储结构如下: typedef int ElemType; typedef struct DulNode { ElemType data; //数据域 DulNode *prior; //指向前驱结点的指针 DulN