数据结构与算法学习02:链表

一、链表

链表是一种动态集合,它是由一系列元素组成的一个列表。

数组也可以实现列表这种数据结构,但数组是静态的,而链表是动态。

通常,我们定义一个结点类(或结构体)来表示链表的元素,它由两部分组成:数值域和指针域。数值域存储当前结点的键,指针域存储下一个节点的地址。

template <class Type>
struct nodeType
{
    Type info;
    nodeType<Type> *link;
};

链表的一些基本操作如下:

  • 初始化为空表
  • 判断是否为空
  • 打印(遍历)链表
  • 计算链表的长度
  • 删除整个链表
  • 获取第一个节点的键
  • 获取最后一个节点的键
  • 查找指定键
  • 插入一个键
  • 删除指定键
  • 复制链表

大致的链表类图如下所示:

与其它的定义可能有所不同,这里维护了两个指针,头指针和尾指针,尾指针是为了方便某些操作,如返回最后一个元素。同时,还维护了一个int型变量来表示链表中元素的个数。这种额外的开销在实际应用时可能不可取,但为某些操作带来很大的便利。

插入和删除以及查找函数定义为纯虚函数,这是因为有序链表和无序链表的相关操作不一样,所以由它们自己实现。这也意味着这个类是抽象类。

注意:上图中有两个陌生的公共成员函数:begin()和end(),它们返回一个迭代器类型(个人喜欢这样叫)的对象。迭代器对象可以很方便地实现链表的遍历等操作,它的类图如下:

下面给出链表类的代码实现:linkedListType.h,这里把结点类和迭代器类的定义也放在这个文件了。由于用到了模板,只能把实现代码也放在定义文件内,看起来会比较长哦。

#ifndef _LINKEDLISTTYPE_H
#define _LINKEDLISTTYPE_H
#include "stdafx.h"

//节点类定义
template <class Type>
struct nodeType
{
    Type info;
    nodeType<Type> *link;
};

//迭代器类定义
template <class Type>
class linkedListIterator
{
public:
    linkedListIterator();    //构造函数
    linkedListIterator(nodeType<Type> *ptr); //复制构造函数
    Type operator*();    //解引用操作符重载
    linkedListIterator<Type> operator++();  //自增操作符重载
    bool operator==(const linkedListIterator<Type>& right) const;   //相等操作符重载
    bool operator!=(const linkedListIterator<Type>& right) const;    //不等操作符重载
private:
    nodeType<Type> *current;   //当前结点
};

//链表类定义
template <class Type>
class linkedListType
{
public:
    linkedListType();    //构造函数
    linkedListType(const linkedListType<Type>& otherList);    //复制构造函数
    ~linkedListType();    //析构函数
    const linkedListType<Type>& operator=(const linkedListType<Type>&);    //重载赋值操作符
    void initializeList();    //初始化为空表
    bool isEmptyList() const;    //判空
    void print() const;      //遍历链表
    int length() const;      //长度
    void destroyList();        //销毁链表
    Type front() const;        //返回第一节点的键
    Type back() const;        //返回最后一个节点的键
    virtual bool search(const Type& searchItem) const = 0;
    virtual void insertFirst(const Type& newItem) = 0;
    virtual void insertLast(const Type& newItem) = 0;
    virtual void deleteNode(const Type& deleteItem) = 0;
    linkedListIterator<Type> begin();
    linkedListIterator<Type> end();
protected:
    int count;
    nodeType<Type> *first;
    nodeType<Type> *last;
private:
    void copyList(const linkedListType<Type>& otherList);
};

//下面是链表类的实现
template <class Type>
linkedListType<Type>::linkedListType()
{
    first = NULL;
    last = NULL;
    count = 0;
}

template <class Type>
linkedListType<Type>::linkedListType(const linkedListType<Type>& otherList)
{
    first = NULL;
    copyList(otherList);
}

template <class Type>
linkedListType<Type>::~linkedListType() //destructor
{
    destroyList();
}

template <class Type>
const linkedListType<Type>& linkedListType<Type>::operator=(const linkedListType<Type>& otherList)
{
    if (this != &otherList)
    {
        copyList(otherList);
    }
    return *this;
}

template <class Type>
bool linkedListType<Type>::isEmptyList() const
{
    return (first == NULL);
}

template <class Type>
void linkedListType<Type>::destroyList()
{
    nodeType<Type> *temp;  //临时保存当前需删除的节点
    while (first != NULL)
    {
        temp = first;
        first = first->link;
        delete temp;
    }
    last = NULL;
    count = 0;
}

template <class Type>
void linkedListType<Type>::initializeList()
{
    destroyList();
}

template <class Type>
void linkedListType<Type>::print() const
{
    nodeType<Type> *current;
    current = first;
    while (current != NULL)
    {
        cout << current->info << " ";
        current = current->link;
    }
}

template <class Type>
int linkedListType<Type>::length() const
{
    return count;
}

template <class Type>
Type linkedListType<Type>::front() const
{
    assert(first != NULL);
    return first->info;
}

template <class Type>
Type linkedListType<Type>::back() const
{
    assert(last != NULL);
    return last->info;
}

template <class Type>
linkedListIterator<Type> linkedListType<Type>::begin()
{
    linkedListIterator<Type> temp(first);
    return temp;
}

template <class Type>
linkedListIterator<Type> linkedListType<Type>::end()
{
    linkedListIterator<Type> temp(NULL);
    return temp;
}

template <class Type>
void linkedListType<Type>::copyList(const linkedListType<Type>& otherList)
{
    nodeType<Type> *newNode; //新建结点
    nodeType<Type> *current; //当前需复制的结点
    if (first != NULL) //先清空
        destroyList();

    if (otherList.first == NULL) //如果otherList是空表
    {
        first = NULL;
        last = NULL;
        count = 0;
    }
    else
    {
        count = otherList.count;//元素个数

        //复制第一个元素
        current = otherList.first;
        first = new nodeType<Type>;
        first->info = current->info;
        first->link = NULL;
        last = first; 

        //复制下一个元素
        current = current->link;
        while (current != NULL)
        {
            newNode = new nodeType<Type>;
            newNode->info = current->info;
            newNode->link = NULL;            

            last->link = newNode;
            last = newNode; 

            current = current->link;
        }
    }
}

//下面是迭代器类的实现
template <class Type>
linkedListIterator<Type>::linkedListIterator()
{
    current = NULL;
}
template <class Type>
linkedListIterator<Type>::linkedListIterator(nodeType<Type> *ptr)
{
    current = ptr;
}
template <class Type>
Type linkedListIterator<Type>::operator*()
{
    return current->info;
}
template <class Type>
linkedListIterator<Type> linkedListIterator<Type>::operator++()
{
    current = current->link;
    return *this;
}
template <class Type>
bool linkedListIterator<Type>::operator==(const linkedListIterator<Type>& right) const
{
    return (current == right.current);
}
template <class Type>
bool linkedListIterator<Type>::operator!=(const linkedListIterator<Type>& right) const
{
    return (current != right.current);
}
#endif

二、无序链表

无序链表是相对有序列表而言的,它们在插入和删除操作的区别非常大,比如:

有序链表每插入一个元素都要找到正确的位置,而无序链表则可以随意在表头或表尾插入。

我们让无序链表继承链表ADT,这样可得到无序链表的类图:

下面是无序链表的编程实现:unorderedLinkedList.h

#include "stdafx.h"
#include "linkedListType.h"

template <class Type>
class unorderedLinkedList: public linkedListType<Type>
{
public:
    bool search(const Type& searchItem) const;    //查找
    void insertFirst(const Type& newItem);        //从表头插入
    void insertLast(const Type& newItem);        //从表尾插入
    void deleteNode(const Type& deleteItem);    //删除
};

template <class Type>
bool unorderedLinkedList<Type>::search(const Type& searchItem)const
{
    nodeType<Type> *current;
    current = first;
    while (current != NULL)
        if (current->info == searchItem)
            return true;
        else
            current = current->link;
    return false;
}

template <class Type>
void unorderedLinkedList<Type>::insertFirst(const Type& newItem)
{
    //从表头插入一个元素
    nodeType<Type> *newNode; //新建一个结点
    newNode = new nodeType<Type>;
    newNode->info = newItem;
    newNode->link = first;
    first = newNode; 

    //总的元素个数加1
    count++; 

    //如果是空表,则最后一个元素就是当前插入的元素,所以让last指向它
    if (last == NULL)
        last = newNode;
}

template <class Type>
void unorderedLinkedList<Type>::insertLast(const Type& newItem)
{
    nodeType<Type> *newNode;
    newNode = new nodeType<Type>;
    newNode->info = newItem;
    newNode->link = NULL;
    if (first == NULL) //空表的情况:first==NULL,这时插入的节点既是头结点也是尾结点
    {
        first = newNode;
        last = newNode;
        count++; //元素个数加一
    }
    else //不为空表的情况:这时只要调整尾结点指针
    {
        last->link = newNode; //从尾部插入
        last = newNode; //调整last指针
        count++;
    }
}

template <class Type>
void unorderedLinkedList<Type>::deleteNode(const Type& deleteItem)
{
    nodeType<Type> *current; //遍历链表用的游标
    nodeType<Type> *trailCurrent; //游标的上一个位置
    bool found;  //标记是否找到指定元素
    if (first == NULL) //情况1:空表
        cout << "Cannot delete from an empty list."<< endl;
    else
    {
        if (first->info == deleteItem) //情况2:第一个元素即为需要删除的元素
        {
            current = first;
            first = first->link;
            count--;
            if (first == NULL) //删除完后检查是否为空:为空则调整last指针
                last = NULL;
            delete current;
        }
        else //情况3:不是第一个元素,那只能一步步往下找了,找到了删掉它,没找到提示用户
        {
            found = false;
            trailCurrent = first; //目前游标的上一个位置是first,即第一个元素
            current = first->link; //目前游标的位置是first后面的元素,即第二个元素

            while (current != NULL && !found)
            {
                if (current->info != deleteItem)
                {
                    trailCurrent = current;
                    current = current-> link;
                }
                else
                    found = true;
            }
            if (found) //情况3.a:找到了,删掉它
            {
                trailCurrent->link = current->link;
                count--;
                if (last == current) //被删元素是最后一个元素,调整last指针
                    last = trailCurrent;
                delete current; //记得释放内存哦
            }
            else  //情况3.b:没有找到,提示用户删除失败
                cout << "The item to be deleted is not in "<< "the list." << endl;
        }
    }
}

三、有序链表

有序链表中的元素都是按顺序链接的,第一个元素最小,最后一个元素最大,可以有重复元素。

让有序链表继承链表类,其类图如下:

与无序链表不同的是它多了一个insert函数,因为有序链表从表头表尾插入都没实际意义,它们都要通过找到合适的位置再插入,所以定义了一个一般的插入方法来实现它们。

有序链表的操作相对无序链表比较复杂一点,下面以插入函数为例介绍分析方法。

插入一个元素:我们按照遍历链表查找第一个大于插入元素的情景进行分类

用一个current指针代表当前的游标元素,用一个trailCurrent指针指向current上一个元素

情况1:表为空,这时直接插入就可以,同时需要调整first和last指针,count+1

情况2:插入元素比表里所有的元素都小,即插入位置为表头,这时插入的同时要调整first指针,同时count+1

情况3:插入元素的位置不不在表头,它在某个地方

3.a:插入元素比表中所有元素都大,也就是说没有找到比他大的元素,这时它插入表尾,需要调整last指针,count+1

3.b: 插入元素在表中间的某个位置。first和last都不需调整,只需count+1

有序链表的编程实现:orderedLinkedList.h

#include "linkedListType.h"

template <class Type>
class orderedLinkedList: public linkedListType<Type>
{
public:
    bool search(const Type& searchItem) const;
    void insert(const Type& newItem);
    void insertFirst(const Type& newItem);
    void insertLast(const Type& newItem);
    void deleteNode(const Type& deleteItem);
};

template <class Type>
bool orderedLinkedList<Type>::search(const Type& searchItem) const
{
    bool found = false;
    nodeType<Type> *current; //遍历链表的游标
    current = first; //从第一个元素开始查找
    while (current != NULL && !found)
        if (current->info >= searchItem)
            found = true;
        else
            current = current->link;
    if (found)
        found = (current->info == searchItem); //测试一下
    return found;
}

template <class Type>
void orderedLinkedList<Type>::insert(const Type& newItem)
{
    nodeType<Type> *current; //遍历链表的游标
    nodeType<Type> *trailCurrent; //游标的上一个元素
    nodeType<Type> *newNode; //新插入的元素
    bool found;//标记是否找到大于newItem的元素
    newNode = new nodeType<Type>;
    newNode->info = newItem;
    newNode->link = NULL; 

    if (first == NULL) //情况1:空表,直接插入
    {
        first = newNode;
        last = newNode;
        count++;
    }
    else
    {
         //从第一个结点开始遍历,看能不能找到值大于newItem的元素,找到了就插入它前面,没找到就插入尾部
        current = first;
        found = false;
        while (current != NULL && !found)
            if (current->info >= newItem)//找到就标记
                found = true;
            else
            {
                trailCurrent = current;
                current = current->link;
            }
            if (current == first) //情况2:第一个元素就大于newItem,插入位置为链表头,需要调整first
            {
                newNode->link = first;
                first = newNode;
                count++;
            }
            else //情况3:插入位置不在链表头,可能在链表尾,即可能需要调整last
            {
                trailCurrent->link = newNode;
                newNode->link = current;
                if (current == NULL)//插入链表尾部,需要调整last
                    last = newNode;
                count++;
            }
    }
}

template <class Type>
void orderedLinkedList<Type>::insertFirst(const Type& newItem)
{
    insert(newItem);
}
template <class Type>
void orderedLinkedList<Type>::insertLast(const Type& newItem)
{
    insert(newItem);
}

template <class Type>
void orderedLinkedList<Type>::deleteNode(const Type& deleteItem)
{
    nodeType<Type> *current; //遍历链表的游标
    nodeType<Type> *trailCurrent; //游标的上一个元素
    bool found;    //标记是否找到等于newItem的元素

    if (first == NULL) //情况1:空表,不能删除
        cout << "Cannot delete from an empty list." << endl;
    else
    {
        //从第一个元素开始向后遍历,看能不能找到等于deleteItem的元素
        current = first;
        found = false;
        while (current != NULL && !found)
            if (current->info >= deleteItem)//注意,我们找到大于等于的元素就标记,这个元素不一定等于deleteItem
                found = true;
            else
            {
                trailCurrent = current;
                current = current->link;
            }
            if (current == NULL) //情况2:没有找到
                cout << "The item to be deleted is not in the "    << "list." << endl;
            else
                if (current->info == deleteItem)
                {
                    if (first == current) //情况3:第一个元素等于newItem
                    {
                        first = first->link;
                        if (first == NULL)//删除完成后判断是否为空,如果为空则调整尾指针
                            last = NULL;
                        delete current;//记得释放内存
                    }
                    else //情况4:不在第一个位置找到了等于newItem的元素
                    {
                        trailCurrent->link = current->link;
                        if (current == last)//如果是最后一个元素,则删除之后要调整尾指针
                            last = trailCurrent;
                        delete current;
                    }
                    count--;
                }
                else
                    cout << "The item to be deleted is not in the "<< "list." << endl;
    }
}
时间: 2024-10-04 11:44:27

数据结构与算法学习02:链表的相关文章

数据结构与算法学习-单向链表的实现

链表(Chain本文所说链表均为单向链表,以下均简称单向链表)实际上是由节点(Node)组成的,一个链表拥有不定数量的节点.而向外暴露的只有一个头节点(Head),我们对链表的所有操作,都是直接或者间接地通过其头节点来进行的. 节点(Node)是由一个需要储存的对象及对下一个节点的引用组成的.也就是说,节点拥有两个成员:储存的对象.对下一个节点的引用. 这样说可能大家不是很明白,我贴一张图大家可能更容易理解. package LinkedList; /** * <p><strong>

java数据结构与算法之双链表设计与实现

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/53047590 出自[zejian的博客] 关联文章: 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) ??上一篇文章分析顺序表和单链表,本篇就接着上篇继续聊链表,在单链表

数据结构与算法JavaScript (三) 链表

数据结构与算法JavaScript (三) 链表 我们可以看到在javascript概念中的队列与栈都是一种特殊的线性表的结构,也是一种比较简单的基于数组的顺序存储结构.由于 javascript的解释器针对数组都做了直接的优化,不会存在在很多编程语言中数组固定长度的问题(当数组填满后再添加就比较困难了,包括添加删除, 都是需要把数组中所有的元素全部都变换位置的,javascript的的数组确实直接给优化好了,如 push,pop,shift,unshift,split方法等等…) 线性表的顺序

数据结构与算法学习之路:背包问题的贪心算法和动态规划算法

一.背包问题描述: 有N种物品和一个重量为M的背包,第i种物品的重量是w[i],价值是p[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大. 二.解决方法: 1.贪心算法:贪心算法基于的思想是每一次选择都作当前最好的选择,这样最后的结果虽然不一定是最优解,但是也不会比最优解差很多. 举个例子说明可能好懂一些:一帮基友去聚餐,菜是一份一份上的,我每一次夹菜都只夹牛肉/海鲜吃,可能到最后我吃的牛肉/海鲜很多,但不一定代表我吃掉的东西的总价值最高,但是相对来说价值也很高

数据结构与算法——学习整理记录

===注:此文由本人结合网上资源整理总结而来,仅代表个人的学习与理解,如有错漏,欢迎指正!=== # 1. 数据结构 ## 1.1 数据结构是什么? 数据结构,直白地理解,就是研究数据的逻辑关系与存储方式的一门学科. 可以简单的分为:数据的逻辑结构(逻辑关系)和数据的存储结构(物理结构). 它是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作. ### 1.1.1 数据的逻辑结构 数据的逻辑结构,简单地理解,就是指的数据之间的逻辑关系. 数据之间的逻辑关系可简单的分为

数据结构和算法学习

Algorithms, 4th Edition 不过一遍都不好意思说你学过算法           学习资料: 怎样学算法? 如何学习数据结构?

数据结构与算法学习之(介绍)

数据结构与算法介绍 数据结构与算法的重要性 算法是程序的灵魂,优秀的程序可以在海量数据计算时,任然保持高速运算 框架和缓存技术的核心时算法 高薪 数据结构与算法的关系 数据(data)结构(Structure)是一门研究组织数据方式的学科,有了编程语言也就有了数据结构 程序 = 数据结构 + 算法 数据结构是算法的基础 数据结构包括线性结构与非线性结构 线性结构 线性结构是最常用的数据结构,特点是元素之间存在一对一的线性关系 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构 线性结构常

数据结构与算法学习(介绍)

数据结构与算法介绍 数据结构与算法的重要性 算法是程序的灵魂,优秀的程序可以在海量数据计算时,任然保持高速运算 框架和缓存技术的核心时算法 高薪 数据结构与算法的关系 数据(data)结构(Structure)是一门研究组织数据方式的学科,有了编程语言也就有了数据结构 程序 = 数据结构 + 算法 数据结构是算法的基础 数据结构包括线性结构与非线性结构 线性结构 线性结构是最常用的数据结构,特点是元素之间存在一对一的线性关系 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构 线性结构常

数据结构和算法学习总结01 绪论

数据结构实际上是数据元素之间的关系的集合 数据结构分为    1.逻辑结构      2.物理结构(逻辑结构的数据元素的存储方式)                            逻辑结构分为  1.集合结构   数据元素无关系,只是属于一个集合                            2.线性结构   数据元素间1对1的关系                            3.树形结构   数据元素间1对多的关系