数据结构基础温故-1.线性表(中)

上一篇中,我们学习了线性表最基础的表现形式-顺序表,但是其存在一定缺点:必须占用一整块事先分配好的存储空间,在插入和删除操作上需要移动大量元素(即操作不方便),于是不受固定存储空间限制并且可以进行比较快捷地插入和删除操作的链表横空出世,所以我们就来复习一下链表。

一、单链表基础

1.1 单链表的节点结构

  在链表中,每个节点由两部分组成:数据域和指针域。

1.2 单链表的总体结构

  链表就是由N个节点链接而成的线性表,如果其中每个节点只包含一个指针域那么就称为单链表,如果含有两个指针域那么就称为双链表。

PS:在线性表的链式存储结构中,为了便于插入和删除操作的实现,每个链表都带有一个头指针(或尾指针),通过头指针可以唯一标识该链表。从头指针所指向的节点出发,沿着节点的链可以访问到每个节点。

二、单链表实现

2.1 单链表节点的定义

    public class Node<T>
    {
        // 数据域
        public T Item { get; set; }
        // 指针域
        public Node<T> Next { get; set; }

        public Node()
        {
        }

        public Node(T item)
        {
            this.Item = item;
        }
    }

  此处定义Node类为单链表的节点,其中包括了一个数据域Item与一个指针域Next(指向后继节点的位置)。

2.2 单链表节点的新增

  ①默认在尾节点后插入新节点

        public void Add(T value)
        {
            Node<T> newNode = new Node<T>(value);
            if (this.head == null)
            {
                // 如果链表当前为空则置为头结点
                this.head = newNode;
            }
            else
            {
                Node<T> prevNode = this.GetNodeByIndex(this.count - 1);
                prevNode.Next = newNode;
            }

            this.count++;
        }    

  首先判断头结点是否为空,其次依次遍历各节点找到尾节点的前驱节点,然后更改前驱节点的Next指针指向新节点即可。

  ②指定在某个节点后插入新节点

        public void Insert(int index, T value)
        {
            Node<T> tempNode = null;
            if (index < 0 || index > this.count)
            {
                throw new ArgumentOutOfRangeException("index", "索引超出范围");
            }
            else if (index == 0)
            {
                if (this.head == null)
                {
                    tempNode = new Node<T>(value);
                    this.head = tempNode;
                }
                else
                {
                    tempNode = new Node<T>(value);
                    tempNode.Next = this.head;
                    this.head = tempNode;
                }
            }
            else
            {
                Node<T> prevNode = GetNodeByIndex(index - 1);
                tempNode = new Node<T>(value);
                tempNode.Next = prevNode.Next;
                prevNode.Next = tempNode;
            }

            this.count++;
        }

  这里需要判断是否是在第一个节点进行插入,如果是则再次判断头结点是否为空。

2.3 单链表节点的移除

        public void RemoveAt(int index)
        {
            if (index == 0)
            {
                this.head = this.head.Next;
            }
            else
            {
                Node<T> prevNode = GetNodeByIndex(index - 1);
                if (prevNode.Next == null)
                {
                    throw new ArgumentOutOfRangeException("index", "索引超出范围");
                }

                Node<T> deleteNode = prevNode.Next;
                prevNode.Next = deleteNode.Next;

                deleteNode = null;
            }

            this.count--;
        }

  移除某个节点只需将其前驱节点的Next指针指向要移除节点的后继节点即可。

  至此,关键部分的代码已介绍完毕,下面给出完整的单链表模拟实现代码:

/// <summary>
/// 单链表模拟实现
/// </summary>
public class MySingleLinkedList<T>
{
        private int count; // 字段:当前链表节点个数
        private Node<T> head; // 字段:当前链表的头结点

        // 属性:当前链表节点个数
        public int Count
        {
            get
            {
                return this.count;
            }
        }

        // 索引器
        public T this[int index]
        {
            get
            {
                return this.GetNodeByIndex(index).Item;
            }
            set
            {
                this.GetNodeByIndex(index).Item = value;
            }
        }

        public MySingleLinkedList()
        {
            this.count = 0;
            this.head = null;
        }

        // Method01:根据索引获取节点
        private Node<T> GetNodeByIndex(int index)
        {
            if (index < 0 || index >= this.count)
            {
                throw new ArgumentOutOfRangeException("index", "索引超出范围");
            }

            Node<T> tempNode = this.head;
            for (int i = 0; i < index; i++)
            {
                tempNode = tempNode.Next;
            }

            return tempNode;
        }

        // Method02:在尾节点后插入新节点
        public void Add(T value)
        {
            Node<T> newNode = new Node<T>(value);
            if (this.head == null)
            {
                // 如果链表当前为空则置为头结点
                this.head = newNode;
            }
            else
            {
                Node<T> prevNode = this.GetNodeByIndex(this.count - 1);
                prevNode.Next = newNode;
            }

            this.count++;
        }

        // Method03:在指定位置插入新节点
        public void Insert(int index, T value)
        {
            Node<T> tempNode = null;
            if (index < 0 || index > this.count)
            {
                throw new ArgumentOutOfRangeException("index", "索引超出范围");
            }
            else if (index == 0)
            {
                if (this.head == null)
                {
                    tempNode = new Node<T>(value);
                    this.head = tempNode;
                }
                else
                {
                    tempNode = new Node<T>(value);
                    tempNode.Next = this.head;
                    this.head = tempNode;
                }
            }
            else
            {
                Node<T> prevNode = GetNodeByIndex(index - 1);
                tempNode = new Node<T>(value);
                tempNode.Next = prevNode.Next;
                prevNode.Next = tempNode;
            }

            this.count++;
        }

        // Method04:移除指定位置的节点
        public void RemoveAt(int index)
        {
            if (index == 0)
            {
                this.head = this.head.Next;
            }
            else
            {
                Node<T> prevNode = GetNodeByIndex(index - 1);
                if (prevNode.Next == null)
                {
                    throw new ArgumentOutOfRangeException("index", "索引超出范围");
                }

                Node<T> deleteNode = prevNode.Next;
                prevNode.Next = deleteNode.Next;

                deleteNode = null;
            }

            this.count--;
        }
    }
}

2.4 单链表的模拟实现简单测试

  这里针对模拟的单链表进行三个简单的测试:一是顺序插入4个节点;二是在指定的位置插入单个节点;三是移除指定位置的单个节点;测试代码如下所示:

    static void MySingleLinkedListTest()
    {
            MySingleLinkedList<int> linkedList = new MySingleLinkedList<int>();
            // Test1:顺序插入4个节点
            linkedList.Add(0);
            linkedList.Add(1);
            linkedList.Add(2);
            linkedList.Add(3);

            Console.WriteLine("The nodes in the linkedList:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            Console.WriteLine("----------------------------");

            // Test2.1:在索引为0(即第1个节点)的位置插入单个节点
            linkedList.Insert(0, 10);
            Console.WriteLine("After insert 10 in index of 0:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            // Test2.2:在索引为2(即第3个节点)的位置插入单个节点
            linkedList.Insert(2, 20);
            Console.WriteLine("After insert 20 in index of 2:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            // Test2.3:在索引为5(即最后一个节点)的位置插入单个节点
            linkedList.Insert(5, 30);
            Console.WriteLine("After insert 30 in index of 5:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            Console.WriteLine("----------------------------");

            // Test3.1:移除索引为5(即最后一个节点)的节点
            linkedList.RemoveAt(5);
            Console.WriteLine("After remove an node in index of 5:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            // Test3.2:移除索引为0(即第一个节点)的节点
            linkedList.RemoveAt(0);
            Console.WriteLine("After remove an node in index of 0:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            // Test3.3:移除索引为2(即第三个节点)的节点
            linkedList.RemoveAt(2);
            Console.WriteLine("After remove an node in index of 2:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.WriteLine(linkedList[i]);
            }
            Console.WriteLine("----------------------------");
        }
        #endregion
    }

  测试结果如下图所示:

  ①顺序插入4个新节点

  ②在指定位置插入新节点

  ③在指定位置移除某个节点

三、双链表基础

3.1 双链表的节点结构

  与单链表不同的是,双链表有两个指针域,一个指向前驱节点,另一个指向后继节点。

3.2 双链表的总体结构

  双链表中,每个节点都有两个指针,指向前驱和后继,这样可以方便地找到某个节点的前驱节点和后继节点,这在某些场合中是非常实用的。

四、双链表实现

4.1 双链表节点的定义

    public class DbNode<T>
    {
        public T Item { get; set; }
        public DbNode<T> Prev { get; set; }
        public DbNode<T> Next { get; set; }

        public DbNode()
        {
        }

        public DbNode(T item)
        {
            this.Item = item;
        }
    }

  与单链表的节点定义不同的是,多了一个指向前驱节点的Prev指针域,可以方便地找到某个节点的前驱节点,从而不必重新遍历一次。

4.2 双链表中插入新节点

  ①默认在尾节点之后插入新节点

        public void AddAfter(T value)
        {
            DbNode<T> newNode = new DbNode<T>(value);
            if (this.head == null)
            {
                // 如果链表当前为空则置为头结点
                this.head = newNode;
            }
            else
            {
                DbNode<T> lastNode = this.GetNodeByIndex(this.count - 1);
                // 调整插入节点与前驱节点指针关系
                lastNode.Next = newNode;
                newNode.Prev = lastNode;
            }
            this.count++;
        }

  ②可选在尾节点之前插入新节点

        public void AddBefore(T value)
        {
            DbNode<T> newNode = new DbNode<T>(value);
            if (this.head == null)
            {
                // 如果链表当前为空则置为头结点
                this.head = newNode;
            }
            else
            {
                DbNode<T> lastNode = this.GetNodeByIndex(this.count - 1);
                DbNode<T> prevNode = lastNode.Prev;
                // 调整倒数第2个节点与插入节点的关系
                prevNode.Next = newNode;
                newNode.Prev = prevNode;
                // 调整倒数第1个节点与插入节点的关系
                lastNode.Prev = newNode;
                newNode.Next = lastNode;
            }
            this.count++;
        }

  典型的四个步骤,调整插入节点与尾节点前驱节点的关系、插入节点与尾节点的关系。

  当然,还可以在指定的位置之前或之后插入新节点,例如InsertAfter和InsertBefore方法,代码详见下面4.3后面的完整实现。

4.3 双链表中移除某个节点

        public void RemoveAt(int index)
        {
            if (index == 0)
            {
                this.head = this.head.Next;
            }
            else
            {
                DbNode<T> prevNode = this.GetNodeByIndex(index - 1);
                if (prevNode.Next == null)
                {
                    throw new ArgumentOutOfRangeException("index", "索引超出范围");
                }

                DbNode<T> deleteNode = prevNode.Next;
                DbNode<T> nextNode = deleteNode.Next;
                prevNode.Next = nextNode;
                if(nextNode != null)
                {
                    nextNode.Prev = prevNode;
                }

                deleteNode = null;
            }
            this.count--;
        }

  这里只需要将前驱节点的Next指针指向待删除节点的后继节点,将后继节点的Prev指针指向待删除节点的前驱节点即可。

  至此,关键部分的代码已介绍完毕,下面给出完整的双链表模拟实现代码:

    /// <summary>
    /// 双链表的模拟实现
    /// </summary>
    public class MyDoubleLinkedList<T>
    {
        private int count; // 字段:当前链表节点个数
        private DbNode<T> head; // 字段:当前链表的头结点

        // 属性:当前链表节点个数
        public int Count
        {
            get
            {
                return this.count;
            }
        }

        // 索引器
        public T this[int index]
        {
            get
            {
                return this.GetNodeByIndex(index).Item;
            }
            set
            {
                this.GetNodeByIndex(index).Item = value;
            }
        }

        public MyDoubleLinkedList()
        {
            this.count = 0;
            this.head = null;
        }

        // Method01:根据索引获取节点
        private DbNode<T> GetNodeByIndex(int index)
        {
            if (index < 0 || index >= this.count)
            {
                throw new ArgumentOutOfRangeException("index", "索引超出范围");
            }

            DbNode<T> tempNode = this.head;
            for (int i = 0; i < index; i++)
            {
                tempNode = tempNode.Next;
            }

            return tempNode;
        }

        // Method02:在尾节点后插入新节点
        public void AddAfter(T value)
        {
            DbNode<T> newNode = new DbNode<T>(value);
            if (this.head == null)
            {
                // 如果链表当前为空则置为头结点
                this.head = newNode;
            }
            else
            {
                DbNode<T> lastNode = this.GetNodeByIndex(this.count - 1);
                // 调整插入节点与前驱节点指针关系
                lastNode.Next = newNode;
                newNode.Prev = lastNode;
            }
            this.count++;
        }

        // Method03:在尾节点前插入新节点
        public void AddBefore(T value)
        {
            DbNode<T> newNode = new DbNode<T>(value);
            if (this.head == null)
            {
                // 如果链表当前为空则置为头结点
                this.head = newNode;
            }
            else
            {
                DbNode<T> lastNode = this.GetNodeByIndex(this.count - 1);
                DbNode<T> prevNode = lastNode.Prev;
                // 调整倒数第2个节点与插入节点的关系
                prevNode.Next = newNode;
                newNode.Prev = prevNode;
                // 调整倒数第1个节点与插入节点的关系
                lastNode.Prev = newNode;
                newNode.Next = lastNode;
            }
            this.count++;
        }

        // Method04:在指定位置后插入新节点
        public void InsertAfter(int index, T value)
        {
            DbNode<T> tempNode;
            if (index == 0)
            {
                if (this.head == null)
                {
                    tempNode = new DbNode<T>(value);
                    this.head = tempNode;
                }
                else
                {
                    tempNode = new DbNode<T>(value);
                    tempNode.Next = this.head;
                    this.head.Prev = tempNode;
                    this.head = tempNode;
                }
            }
            else
            {
                DbNode<T> prevNode = this.GetNodeByIndex(index); // 获得插入位置的节点
                DbNode<T> nextNode = prevNode.Next; // 获取插入位置的后继节点
                tempNode = new DbNode<T>(value);
                // 调整插入节点与前驱节点指针关系
                prevNode.Next = tempNode;
                tempNode.Prev = prevNode;
                // 调整插入节点与后继节点指针关系
                if (nextNode != null)
                {
                    tempNode.Next = nextNode;
                    nextNode.Prev = tempNode;
                }
            }
            this.count++;
        }

        // Method05:在指定位置前插入新节点
        public void InsertBefore(int index, T value)
        {
            DbNode<T> tempNode;
            if (index == 0)
            {
                if (this.head == null)
                {
                    tempNode = new DbNode<T>(value);
                    this.head = tempNode;
                }
                else
                {
                    tempNode = new DbNode<T>(value);
                    tempNode.Next = this.head;
                    this.head.Prev = tempNode;
                    this.head = tempNode;
                }
            }
            else
            {
                DbNode<T> nextNode = this.GetNodeByIndex(index); // 获得插入位置的节点
                DbNode<T> prevNode = nextNode.Prev; // 获取插入位置的前驱节点
                tempNode = new DbNode<T>(value);
                // 调整插入节点与前驱节点指针关系
                prevNode.Next = tempNode;
                tempNode.Prev = prevNode;
                // 调整插入节点与后继节点指针关系
                tempNode.Next = nextNode;
                nextNode.Prev = tempNode;
            }
            this.count++;
        }

        // Method06:移除指定位置的节点
        public void RemoveAt(int index)
        {
            if (index == 0)
            {
                this.head = this.head.Next;
            }
            else
            {
                DbNode<T> prevNode = this.GetNodeByIndex(index - 1);
                if (prevNode.Next == null)
                {
                    throw new ArgumentOutOfRangeException("index", "索引超出范围");
                }

                DbNode<T> deleteNode = prevNode.Next;
                DbNode<T> nextNode = deleteNode.Next;
                prevNode.Next = nextNode;
                if(nextNode != null)
                {
                    nextNode.Prev = prevNode;
                }

                deleteNode = null;
            }
            this.count--;
        }
    }

4.4 双链表模拟实现的简单测试

  这里跟单链表一样,进行几个简单的测试:一是顺序插入(默认在尾节点之后)4个新节点,二是在尾节点之前和在指定索引位置插入新节点,三是移除指定索引位置的节点,四是修改某个节点的Item值。测试代码如下所示。

        static void MyDoubleLinkedListTest()
        {
            MyDoubleLinkedList<int> linkedList = new MyDoubleLinkedList<int>();
            // Test1:顺序插入4个节点
            linkedList.AddAfter(0);
            linkedList.AddAfter(1);
            linkedList.AddAfter(2);
            linkedList.AddAfter(3);

            Console.WriteLine("The nodes in the DoubleLinkedList:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            Console.WriteLine("----------------------------");
            // Test2.1:在尾节点之前插入2个节点
            linkedList.AddBefore(10);
            linkedList.AddBefore(20);
            Console.WriteLine("After add 10 and 20:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            // Test2.2:在索引为2(即第3个节点)的位置之后插入单个节点
            linkedList.InsertAfter(2, 50);
            Console.WriteLine("After add 50:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            // Test2.3:在索引为2(即第3个节点)的位置之前插入单个节点
            linkedList.InsertBefore(2, 40);
            Console.WriteLine("After add 40:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            Console.WriteLine("----------------------------");
            // Test3.1:移除索引为7(即最后一个节点)的位置的节点
            linkedList.RemoveAt(7);
            Console.WriteLine("After remove an node in index of 7:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            // Test3.2:移除索引为0(即第一个节点)的位置的节点的值
            linkedList.RemoveAt(0);
            Console.WriteLine("After remove an node in index of 0:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            // Test3.3:移除索引为2(即第3个节点)的位置的节点
            linkedList.RemoveAt(2);
            Console.WriteLine("After remove an node in index of 2:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            Console.WriteLine("----------------------------");
            // Test4:修改索引为2(即第3个节点)的位置的节点的值
            linkedList[2] = 9;
            Console.WriteLine("After update the value of node in index of 2:");
            for (int i = 0; i < linkedList.Count; i++)
            {
                Console.Write(linkedList[i] + " ");
            }
            Console.WriteLine();
            Console.WriteLine("----------------------------");
        }

  测试结果如下图所示。

五、.NET中的ListDictionary与LinkedList<T>

  在.NET中,已经为我们提供了单链表和双链表的实现,它们分别是ListDictionary与LinkedList<T>。从名称可以看出,单链表的实现ListDictionary不是泛型实现,而LinkedList是泛型实现,它们又到底有什么区别呢,借助Reflector去看看吧。

5.1 ListDictionary—基于key/value的单链表

  ListDictionary位于System.Collection.Specialized下,它是基于键值对(Key/Value)的集合,微软给出的建议是:通常用于包含10个或10个以下项的集合。

  它的节点的数据域是一个键值对,而不是一个简单的value。

5.2 LinkedList—神奇的泛型双向链表

  在.NET中,LinkedList<T>是使用地比较多的链表实现类,它位于System.Collections.Generic下,是一个通用的双向链表类,它不支持随机访问(即索引访问),但它实现了很多的新增节点的方法,例如:AddAfter、AddBefore、AddFirst以及AddLast等。其中,AddFirst是在现有节点之后添加新节点,AddBefore则是在现有节点之前添加新节点,AddFirst是在开头处添加,而AddLast则是在末尾处添加。

参考资料

(1)程杰,《大话数据结构》

(2)陈广,《数据结构(C#语言描述)》

(3)段恩泽,《数据结构(C#语言版)》

(4)率辉,《数据结构高分笔记(2015版)》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

时间: 2024-10-08 09:45:39

数据结构基础温故-1.线性表(中)的相关文章

数据结构基础温故-1.线性表(上)

开篇:线性表是最简单也是在编程当中使用最多的一种数据结构.例如,英文字母表(A,B,C,D...,Z)就是一个线性表,表中的每一个英文字母都是一个数据元素:又如,成绩单也是一个线性表,表中的每一行是一个数据元素,每个数据元素又由学号.姓名.成绩等数据项组成.顺序表和链表作为线性表的两种重要的存在形式,它们是堆栈.队列.树.图等数据结构的实现基础. 一.线性表基础 1.1 线性表的基本定义 线性表:零个或多个数据元素的有限序列.线性表中的元素在位置上是有序的,类似于储户去银行排队取钱,人们依次排着

数据结构基础温故-1.线性表(下)

在上一篇中,我们了解了单链表与双链表,本次将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list). 一.循环链表基础 1.1 循环链表节点结构 循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断p.next是否为空,现在则是p.next不等于头结点,则循环未结束. 1.2 循环链表的O(1)访问时间 在单链表中,有了头结点,我们可以在O(1)时间访问到第一个节点,但如果要访

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

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

小猪的数据结构辅助教程——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.抽象的数据类型 简单点说: 抽象:有点像我们面向对象语言中的类的思想,将事物

数据结构基础温故-5.图(中):最小生成树算法

图的“多对多”特性使得图在结构设计和算法实现上较为困难,这时就需要根据具体应用将图转换为不同的树来简化问题的求解. 一.生成树与最小生成树 1.1 生成树 对于一个无向图,含有连通图全部顶点的一个极小连通子图成为生成树(Spanning Tree).其本质就是从连通图任一顶点出发进行遍历操作所经过的边,再加上所有顶点构成的子图. 采用深度优先遍历获得的生成树称为深度优先生成树(DFS生成树),采用广度优先遍历获得的生成树称为广度优先生成树(BFS生成树).如下图所示,无向图的DFS生成树和BFS

数据结构基础温故-6.查找(上):基本查找与树表查找

只要你打开电脑,就会涉及到查找技术.如炒股软件中查股票信息.硬盘文件中找照片.在光盘中搜DVD,甚至玩游戏时在内存中查找攻击力.魅力值等数据修改用来作弊等,都要涉及到查找.当然,在互联网上查找信息就更加是家常便饭.查找是计算机应用中最常用的操作之一,也是许多程序中最耗时的一部分,查找方法的优劣对于系统的运行效率影响极大.因此,本篇讨论一些查找方法. 一.顺序查找 1.1 基本思想 顺序查找(Sequential Search)又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个(或最后

王道数据结构——线性表中顺序表的一些综合应用题

1.长度为n的顺序表中,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,用于删除线性表中所有值为x的数据元素.(满足要求的数放在第k位上) 1 #include <cstdio> 2 3 /*输出数组名为a.长度为n的数组*/ 4 void print(int *a, int n){ 5 for(int i = 0;i < n; i++){ 6 printf("%d ", a[i]); 7 } 8 puts(""); 9 } 10 11