• 数据结构之链表


    在面试过程中,数据结构和算法基本上算是研发类岗位必考的部分,而链表基本上又是数据结构中相对容易掌握、而且容易出题的部分,因此我们先整理一下链表部分的经典题目。

    (声明:以下所有程序都是用java编写)

    首先,我们来定义一个链表的数据结构,如下:

    View Code

    有了这个数据结构后,我们需要一个方法来生成和输出链表,其中链表中每个元素的值采用的是随机数。

    生成链表的代码如下:

    View Code
     1     public static Link init(int count, int maxValue)
     2     {
     3         Link list = new Link();
     4         Link temp = list;
     5         Random r = new Random();
     6         temp.set_Value(Integer.MIN_VALUE);
     7         for (int i = 0; i < count; i++)
     8         {
     9             Link node = new Link();
    10             node.set_Value(r.nextInt(maxValue));
    11             temp.set_Next(node);
    12             temp=node;
    13         }
    14         temp.set_Next(null);
    15         return list;
    16     }
    17     
    18     public static Link init(int count)
    19     {
    20         return init(count, Integer.MAX_VALUE);
    21     }

    对于链表的头结点,我们是不存储任何信息的,因此将其值设置为Integer.MIN_VALUE。我们重载了生成链表的方法。

    下面是打印链表信息的方法:

    View Code
     1     public static void printList(Link list)
     2     {
     3         if (list == null || list.get_Next() == null)
     4         {
     5             System.out.println("The list is null or empty.");
     6             return;
     7         }
     8         Link temp = list.get_Next();
     9         StringBuffer sb = new StringBuffer();
    10         while(temp != null)
    11         {
    12             sb.append(temp.get_Value() + "->");
    13             temp=temp.get_Next();
    14         }
    15         System.out.println(sb.substring(0, sb.length() - 2));
    16     }

    好了,有了以上这些基础的方法, 我们就可以深入探讨链表相关的面试题了。

    • 链表反转 思路:有两种方法可以实现链表反转,第一种是直接循环每个元素,修改它的Next属性;另一种是采取递归的方式。 首先来看直接循环的方式:
      View Code
       1     public static Link Reverve(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             System.out.println("list is null or just contains 1 element, so do not need to reverve.");
       6             return list;
       7         }        
       8         Link current = list.get_Next();
       9         Link next = current.get_Next();
      10         current.set_Next(null);
      11         while(next != null)
      12         {
      13             Link temp = next.get_Next();
      14             next.set_Next(current);
      15             current = next;
      16             next = temp;
      17         }
      18         list.set_Next(current);
      19         
      20         return list;
      21     }

      然后是递归方式:

      View Code
       1     public static Link RecursiveReverse(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             System.out.println("list is null or just contains 1 element, so do not need to reverve.");
       6             return list;
       7         }
       8         
       9         list.set_Next(Recursive(list.get_Next()));
      10         
      11         return list;
      12     }
      13     
      14     
      15     private static Link Recursive(Link list)
      16     {
      17         if (list.get_Next() == null)
      18         {
      19             return list;
      20         }
      21         Link temp = Recursive(list.get_Next());
      22         list.get_Next().set_Next(list);
      23         list.set_Next(null);
      24         
      25         return temp;
      26 
    • 输出指定位置的元素(倒数第N个元素) 思路:采用两个游标来遍历链表,第1个游标先走N步,然后两个游标同时前进,当第一个游标到最后时,第二个游标就是想要的元素。
      View Code
       1     public static Link find(Link list, int rPos)
       2     {
       3         if (list == null || list.get_Next() == null)
       4         {
       5             return null;
       6         }
       7         int i = 1;
       8         Link first = list.get_Next();
       9         Link second = list.get_Next();
      10         while(true)
      11         {
      12             if (i==rPos || first == null) break;
      13             first = first.get_Next();
      14             i++;
      15         }
      16         if (first == null)
      17         {
      18             System.out.println("The length of list is less than " + rPos + ".");
      19             return null;
      20         }
      21         while(first.get_Next() != null)
      22         {
      23             first = first.get_Next();
      24             second = second.get_Next();
      25         }
      26         
      27         return second;
      28     }
    • 删除指定节点 思路:可以分情况讨论,如果指定节点不是尾节点,那么可以采用取巧的方式,将指定节点的值修改为下一个节点的值,将指定节点的Next属性设置为Next.Next;但如果指定节点为尾节点,那么只能是从头开始遍历。
      View Code
       1     public static void delete(Link list, Link element)
       2     {
       3         if (element.get_Next() != null)
       4         {
       5             element.set_Value(element.get_Next().get_Value());
       6             element.set_Next(element.get_Next().get_Next());
       7         }
       8         else
       9         {
      10             Link current = list.get_Next();
      11             while(current.get_Next() != element)
      12             {
      13                 current = current.get_Next();
      14             }
      15             current.set_Next(null);
      16         }
      17     }
    • 删除重复节点 思路:采用hashtable来存取链表中的元素,遍历链表,当指定节点的元素在hashtable中已经存在,那么删除该节点。
      View Code
       1     public static void removeDuplicate(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) return;
       4         Hashtable table = new Hashtable();
       5         Link cur = list.get_Next();
       6         Link next = cur.get_Next();
       7         table.put(cur.get_Value(), 1);
       8         while(next != null)
       9         {
      10             if (table.containsKey(next.get_Value()))
      11             {
      12                 cur.set_Next(next.get_Next());
      13                 next = next.get_Next();                 
      14             }
      15             else
      16             {
      17                 table.put(next.get_Value(), 1);
      18                 cur= next;
      19                 next = next.get_Next();
      20             }
      21             
      22         }        
      23     }
    • 寻找链表中间节点 思路:采用两个游标的方式,第一个游标每次前进两步,第二个游标每次前进一步,当第一个游标到最后时,第二个游标就是中间位置。需要注意的是,如果链表元素的个数是偶数,那么中间元素应该是两个。
      View Code
       1     public static void findMiddleElement(Link list)
       2     {
       3         if (list == null || list.get_Next() == null) return;
       4         System.out.println("The Middle element is:");
       5         if (list.get_Next().get_Next() == null)
       6         {
       7             System.out.println(list.get_Next().get_Value());
       8         }
       9         Link fast = list.get_Next();
      10         Link slow = list.get_Next();
      11         while(fast.get_Next() != null && fast.get_Next().get_Next() != null)
      12         {
      13             fast = fast.get_Next().get_Next();
      14             slow = slow.get_Next();
      15         }
      16             
      17         if (fast != null && fast.get_Next() == null)
      18         {
      19             System.out.println(slow.get_Value());
      20         }
      21         else
      22         {
      23             System.out.println(slow.get_Value());
      24             System.out.println(slow.get_Next().get_Value());
      25         }
      26     }
    • 链表元素排序 思路:链表元素排序,有两种方式,一种是链表元素本身的排序,一种是链表元素值得排序。第二种方式更简单、灵活一些。
      View Code
       1     public static void Sort(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             return;
       6         }
       7         Link current = list.get_Next();
       8         Link next = current.get_Next();
       9         while(current.get_Next() != null)
      10         {
      11             while(next != null)
      12             {
      13                 if (current.get_Value() > next.get_Value())
      14                 {
      15                     int temp = current.get_Value();
      16                     current.set_Value(next.get_Value());
      17                     next.set_Value(temp);
      18                 }
      19                 next = next.get_Next();
      20             }
      21             current = current.get_Next();
      22             next = current.get_Next();
      23         }
      24     }
    • 判断链表是否有环,如果有,找出环上的第一个节点 思路:可以采用两个游标的方式判断链表是否有环,一个游标跑得快,一个游标跑得慢。当跑得快的游标追上跑得慢的游标时,说明有环;当跑得快的游标跑到尾节点时,说明无环。 至于如何找出换上第一个节点,可以分两步,首先确定环上的某个节点,计算头结点到该节点的距离以及该节点在环上循环一次的距离,然后建立两个游标,分别指向头结点和环上的节点,并将距离平摊(哪个距离大,先移动哪个游标,直至两个距离相等),最后同时移动两个游标,碰到的第一个相同元素,就是环中的第一个节点。
      View Code
       1     public static Link getLoopStartNode(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             return null;
       6         }
       7         int m = 1, n = 1;
       8         Link fast = list.get_Next();
       9         Link slow = list.get_Next();
      10         while(fast != null && fast.get_Next() != null)
      11         {
      12             fast = fast.get_Next().get_Next();
      13             slow = slow.get_Next();
      14             if (fast == slow) break;
      15             m++;
      16         }
      17         if (fast != slow)
      18         {
      19             return null;
      20         }
      21         Link temp = fast;
      22         while(temp.get_Next() != fast)
      23         {
      24             temp = temp.get_Next();
      25             n++;
      26         }
      27         Link node1 = list.get_Next();
      28         Link node2 = fast;
      29         if (m < n)
      30         {
      31             for (int i = 0; i < n - m; i++)
      32             {
      33                 node2 = node2.get_Next();
      34             }
      35         }
      36         if (m > n)
      37         {
      38             for (int i = 0; i < m - n; i++)
      39             {
      40                 node1 = node1.get_Next();
      41             }
      42         }
      43         while(true)
      44         {
      45             if (node1 == node2)
      46             {
      47                 break;
      48             }
      49             node1 = node1.get_Next();
      50             node2 = node2.get_Next();
      51         }
      52         
      53         return node1;
      54         
      55     }
    • 判断两个链表是否相交 思路:判断两个链表的尾节点是否相同,如果相同,一定相交
      View Code
       1     public static boolean isJoint(Link list1, Link list2)
       2     {
       3         if (list1 == null || list2 == null || list1.get_Next() == null || list2.get_Next() == null)
       4         {
       5             return false;
       6         }
       7         Link node1 = list1;
       8         Link node2 = list2;
       9         while(node1.get_Next() != null)
      10         {
      11             node1 = node1.get_Next();
      12         }
      13         while(node2.get_Next() != null)
      14         {
      15             node2 = node2.get_Next();
      16         }
      17         
      18         return node1 == node2;
      19     }
    • 合并两个有序链表 思路:新建一个链表,然后同时遍历两个有序链表,比较其大小,将元素较小的链表向前移动,直至某一个链表元素为空。然后将非空链表上的所有元素追加到新建链表中。
      View Code
       1     public static Link merge(Link list1, Link list2)
       2     {
       3         Link list = new Link();
       4         list.set_Value(Integer.MIN_VALUE);
       5         Link current1 = list1.get_Next();
       6         Link current2 = list2.get_Next();
       7         Link current = list;
       8         while(current1 != null && current2 != null)
       9         {
      10             Link temp = new Link();
      11             if (current1.get_Value() > current2.get_Value())
      12             {
      13                 temp.set_Value(current2.get_Value());
      14                 current2 = current2.get_Next();
      15             }
      16             else
      17             {
      18                 temp.set_Value(current1.get_Value());
      19                 current1 = current1.get_Next();
      20             }
      21             current.set_Next(temp);
      22             current = temp;
      23         }
      24         if (current1 != null)
      25         {
      26             while(current1 != null)
      27             {
      28                 Link temp = new Link();
      29                 temp.set_Value(current1.get_Value());
      30                 current.set_Next(temp);
      31                 current = temp;
      32                 current1 = current1.get_Next();
      33             }
      34         }
      35         
      36         if (current2 != null)
      37         {
      38             while(current2 != null)
      39             {
      40                 Link temp = new Link();
      41                 temp.set_Value(current2.get_Value());
      42                 current.set_Next(temp);
      43                 current = temp;
      44                 current2 = current2.get_Next();
      45             }
      46         }
      47         
      48         current.set_Next(null);
      49         
      50         return list;
      51     }
    • 交换链表中任意两个元素(非头结点) 思路:首先需要保存两个元素的pre节点和next节点,然后分别对pre节点和next节点的Next属性重新赋值。需要注意的是,当两个元素师相邻元素时,需要特殊处理,否则会将链表陷入死循环。
      View Code
       1     public static void swap(Link list, Link element1, Link element2)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null || 
       4                 element1 == null || element2 == null || element1 == element2) 
       5             return;
       6                 
       7         Link pre1 = null, pre2 = null, next1 = null, next2 = null;
       8         Link cur1=element1, cur2=element2;
       9         Link temp = list.get_Next();
      10         boolean bFound1 = false;
      11         boolean bFound2 = false;
      12         while(temp != null)
      13         {
      14             if(temp.get_Next() == cur1)
      15             {
      16                 pre1=temp;
      17                 next1 = temp.get_Next().get_Next();
      18                 bFound1 = true;
      19             }
      20             if (temp.get_Next() == cur2)
      21             {
      22                 pre2 = temp;
      23                 next2 = temp.get_Next().get_Next();
      24                 bFound2=true;
      25             }
      26             if (bFound1 && bFound2) break;
      27             temp = temp.get_Next();
      28         }
      29         
      30         if (cur1.get_Next() == cur2)
      31         {
      32             temp = cur2.get_Next();
      33             pre1.set_Next(cur2);
      34             cur2.set_Next(cur1);
      35             cur1.set_Next(temp);
      36         }
      37         else if (cur2.get_Next() == cur1)
      38         {
      39             temp = cur1.get_Next();
      40             pre2.set_Next(cur1);
      41             cur1.set_Next(cur2);
      42             cur2.set_Next(temp);
      43         }
      44         else
      45         {
      46             pre1.set_Next(cur2);
      47             cur1.set_Next(next2);
      48             pre2.set_Next(cur1);
      49             cur2.set_Next(next1);
      50         }
      51     }

      这里,还有另外一种取巧的方法,就是直接交换两个元素的值,而不需要修改引用。

      View Code
       1     public static void swapValue(Link list, Link element1, Link element2)
       2     {
       3         if (element1 == null || element2 == null)
       4         {
       5             return;
       6         }
       7         int temp = element1.get_Value();
       8         element1.set_Value(element2.get_Value());
       9         element2.set_Value(temp);
      10     }

      不过,这种方式,应该不是面试官所希望看到的。

    定义

    数据结构名词定义

    • 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。记为:
    Data_Structure=(D,R)
    其中D是数据元素的集合R是该集合中所有元素之间的关系的有限集合

    数据结构其它定义

    Sartaj Sahni在他的《数据结构、算法与应用》一书中称:“数据结构是数据对象,以及存在于该对象的实例和组成实 例的数据元素之间的各种联系。这些联系可以通过定义相关的函数来给出。”他将数据对象(data object)定义为“一个数据对象是实例的集合”。
    Clifford A.Shaffer在《数据结构与算法分析》一书中的定义是:“数据结构是ADT抽象数据类型Abstract Data Type) 的物理实现。”
    Robert L.Kruse在《数据结构与程序设计》一书中,将一个数据结构的设计过程分成抽象层、数据结构层和实现层。其中,抽象层是指抽象数据类型层,它讨论数据的逻辑结构及其运算,数据结构层和实现层讨论一个数据结构的表示和在计算机内的存储细节以及运算的实现。
    数据结构具体指同一类数据元素中,各元素之间的相互关系,包括三个组成成分,数据的逻辑结构数据的存储结构和数据运算结构。

    数据结构研究对象

    一、数据的逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关。逻辑结构包括:
    1. 集合
    数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系;
    数据结构中的元素存在一对一的相互关系;
    数据结构中的元素存在一对多的相互关系;
    数据结构中的元素存在多对多的相互关系。
    二、数据的物理结构:指数据的逻辑结构在计算机存储空间的存放形式。
    数据的物理结构是数据结构在计算机中的表示(又称映像),它包括数据元素的机内表示和关系的机内表示。由于具体实现的方法有顺序、链接、索引、散列等多种,所以,一种数据结构可表示成一种或多种存储结构。
    数据元素的机内表示(映像方法): 用二进制位(bit)的位串表示数据元素。通常称这种位串为节点(node)。当数据元素有若干个数据项组成时,位串中与个数据项对应的子位串称为数据域(data field)。因此,节点是数据元素的机内表示(或机内映像)。
    关系的机内表示(映像方法):数据元素之间的关系的机内表示可以分为顺序映像和非顺序映像,常用两种存储结构:顺序存储结构和链式存储结构。顺序映像借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。非顺序映像借助指示元素存储位置的指针(pointer)来表示数据元素之间的逻辑关系。
    三、数据结构的运算

    数据结构重要意义

    一般认为,一个数据结构是由数据元素依据某种逻辑联系组织起来的。对数据元素间逻辑关系的描述称为数据的逻辑结构;数据必须在计算机内存储,数据的存储结构是数据结构的实现形式,是其在计算机内的表示;此外讨论一个数据结构必须同时讨论在该类数据上执行的运算才有意义。一个逻辑数据结构可以有多种存储结构,且各种存储结构影响数据处理的效率。
    在许多类型的程序的设计中,数据结构的选择是一个基本的设计考虑因素。许多大型系统的构造经验表明,系统实现的困难程度和系统构造的质量都严重的依赖于是否选择了最优的数据结构。许多时候,确定了数据结构后,算法就容易得到了。有些时候事情也会反过来,我们根据特定算法来选择数据结构与之适应。不论哪种情况,选择合适的数据结构都是非常重要的。
    选择了数据结构,算法也随之确定,是数据而不是算法是系统构造的关键因素。这种洞见导致了许多种软件设计方法和程序设计语言的出现,面向对象的程序设计语言就是其中之一。

    数据结构研究内容

    在计算机科学中,数据结构是一门研究非数值计算的程序设计问题中计算机的操作对象(数据元素)以及它们之间的关系和运算等的学科,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。
    “数据结构”作为一门独立的课程在国外是从1968年才开始设立的。 1968年美国唐纳德·克努特(Donald Ervin Knuth)教授开创了数据结构的最初体系,他所著的《计算机程序设计艺术》第一卷《基本算法》是第一本较系统地阐述数据的逻辑结构和存储结构及其操作的著作。“数据结构”在计算机科学中是一门综合性的专业基础课,数据结构是介于数学、计算机硬件和计算机软件三者之间的一门核心课程。数据结构这一门课的内容不仅是一般程序设计(特别是非数值性程序设计)的基础,而且是设计和实现编译程序、操作系统、数据库系统及其他系统程序的重要基础。
    计算机科学是一门研究用计算机进行信息表示和处理的科学。这里面涉及到两个问题:信息的表示,信息的处理 。
    而信息的表示和组织又直接关系到处理信息的程序的效率。随着计算机的普及,信息量的增加,信息范围的拓宽,使许多系统程序和应用程序的规模很大,结构又相当复杂。因此,为了编写出一个“好”的程序,必须分析待处理的对象的特征及各对象之间存在的关系,这就是数据结构这门课所要研究的问题。众所周知,计算机的程序是对信息进行加工处理。在大多数情况下,这些信息并不是没有组织,信息(数据)之间往往具有重要的结构关系,这就是数据结构的内容。数据的结构,直接影响算法的选择和效率。
    计算机解决一个具体问题时,大致需要经过下列几个步骤:首先要从具体问题中抽象出一个适当的数学模型,然后设计一个解此数学模型的算法(Algorithm),最后编出程序、进行测试、调整直至得到最终解答。
    寻求数学模型的实质是分析问题,从中提取操作的对象,并找出这些操作对象之间含有的关系,然后用数学的语言加以描述。当人们用计算机处理数值计算问题是,所用的数学模型是用数学方程描述。所涉及的运算对象一般是简单的整形、实型和逻辑型数据,因此程序设计者的主要精力集中于程序设计技巧上,而不是数据的存储和组织上。然而,计算机应用的更多领域是“非数值型计算问题”,它们的数学模型无法用数学方程描述,而是用数据结构描述,解决此类问题的关键是设计出合适的数据结构,描述非数值型问题的数学模型是用线性表、树、图等结构来描述的。
    计算机算法与数据的结构密切相关,算法无不依附于具体的数据结构,数据结构直接关系到算法的选择和效率。运算是由计算机来完成,这就要设计相应的插入、删除和修改的算法 。也就是说,数据结构还需要给出每种结构类型所定义的各种运算的算法。
    数据是信息的载体,是可以被计算机识别存储并加工处理的描述客观事物的信息符号的总称。所有能被输入计算机中,且能被计算机处理的符号的集合,它是计算机程序加工处理的对象。客观事物包括数值、字符、声音、图形、图像等,它们本身并不是数据,只有通过编码变成能被计算机识别、存储和处理的符号形式后才是数据。
    数据元素是数据的基本单位,在计算机程序中通常作为一个整体考虑。一个数据元素由若干个数据项组成。数据项是数据结构中讨论的最小单位。有两类数据元素:若数据元素可再分,则每一个独立的处理单元就是数据项,数据元素是数据项的集合;若数据元素不可再分,则数据元素和数据项是同一概念,如:整数"5",字符 "N" 等。例如描述一个学生的信息的数据元素可由下列6个数据项组成。其中的出生日期又可以由三个数据项:"年"、"月"和"日"组成,则称"出生日期"为组合项,而其它不可分割的数据项为原子项。
    关键字指的是能识别一个或多个数据元素的数据项。若能起唯一识别作用,则称之为 "主" 关键字,否则称之为 "次" 关键字。
    数据对象是性质相同的数据元素的集合,是数据的一个子集。数据对象可以是有限的,也可以是无限的。
    数据处理是指对数据进行查找插入、删除、合并、排序、统计以及简单计算等的操作过程。在早期,计算机主要用于科学和工程计算,进入八十年代以后,计算机主要用于数据处理。据有关统计资料表明,计算机用于数据处理的时间比例达到80%以上,随着时间的推移和计算机应用的进一步普及,计算机用于数据处理的时间比例必将进一步增大。

    数据结构结构分类

    数据结构是指同一数据元素类中各数据元素之间存在的关系。数据结构分别为逻辑结构存储结构物理结构)和数据的运算。数据的逻辑结构是从具体问题抽象出来的数学模型,是描述数据元素及其关系的数学特性的,有时就把逻辑结构简称为数据结构。逻辑结构是在计算机存储中的映像,形式地定义为(K,R)(或(D,S)),其中,K是数据元素的有限集,R是K上的关系的有限集。
    根据数据元素间关系的不同特性,通常有下列四类基本的结构:
    ⑴集合结构。该结构的数据元素间的关系是“属于同一个集合”。
    ⑵线性结构。该结构的数据元素之间存在着一对一的关系。
    ⑶树型结构。该结构的数据元素之间存在着一对多的关系。
    ⑷图形结构。该结构的数据元素之间存在着多对多的关系,也称网状结构。 从上面所介绍的数据结构的概念中可以知道,一个数据结构有两个要素。一个是数据元素的集合,另一个是关系的集合。在形式上,数据结构通常可以采用一个二元组来表示。
    数据结构的形式定义为:数据结构是一个二元组 :Data_Structure=(D,R),其中,D是数据元素的有限集,R是D上关系的有限集。线性结构的特点是数据元素之间是一种线性关系,数据元素“一个接一个的排列”。在一个线性表数据元素的类型是相同的,或者说线性表是由同一类型的数据元素构成的线性结构。在实际问题中线性表的例子是很多的,如学生情况信息表是一个线性表:表中数据元素的类型为学生类型; 一个字符串也是一个线性表:表中数据元素的类型为字符型,等等。
    线性表是最简单、最基本、也是最常用的一种线性结构。 线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,通常记为: (a1,a2,… ai-1,ai,ai+1,…an) ,其中n为表长, n=0 时称为空表。 它有两种存储方法:顺序存储和链式存储,它的主要基本操作是插入、删除和检索等。
    数据结构在计算机中的表示(映像)称为数据的物理(存储)结构。它包括数据元素的表示和关系的表示。数据元素之间的关系有两种不同的表示方法:顺序映象和非顺序映象,并由此得到两种不同的存储结构:顺序存储结构和链式存储结构
    顺序存储方法:它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现,由此得到的存储表示称为顺序存储结构。顺序存储结构是一种最基本的存储表示方法,通常借助于程序设计语言中的数组来实现。
    链接存储方法:它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。由此得到的存储表示称为链式存储结构,链式存储结构通常借助于程序设计语言中的指针类型来实现
    索引存储方法:除建立存储结点信息外,还建立附加的索引表来标识结点的地址
    散列存储方法:就是根据结点的关键字直接计算出该结点的存储地址。
    数据结构中,逻辑上(逻辑结构:数据元素之间的逻辑关系)可以把数据结构分成线性结构和非线性结构。线性结构的顺序存储结构是一种顺序存取的存储结构,线性表的链式存储结构是一种随机存取的存储结构。线性表若采用链式存储表示时所有结点之间的存储单元地址可连续可不连续。逻辑结构与数据元素本身的形式、内容、相对位置、所含结点个数都无关。

    数据结构结构算法

    算法的设计取决于数据(逻辑)结构,而算法的实现依赖于采用的存储结构。数据的存储结构实质上是它的逻辑结构在计算机存储器中的实现,为了全面的反映一个数据的逻辑结构,它在存储器中的映象包括两方面内容,即数据元素之间的信息和数据元素之间的关系。不同数据结构有其相应的若干运算。数据的运算是在数据的逻辑结构上定义的操作算法,如检索、插入、删除、更新和排序等。
    数据的运算是数据结构的一个重要方面,讨论任一种数据结构时都离不开对该结构上的数据运算及其实现算法的讨论。
    数据结构不同于数据类型,也不同于数据对象,它不仅要描述数据类型的数据对象,而且要描述数据对象各元素之间的相互关系。
    数据类型是一个值的集合和定义在这个值集上的一组操作的总称。数据类型可分为两类:原子类型、结构类型。一方面,在程序设计语言中,每一个数据都属于某种数据类型。类型明显或隐含地规定了数据的取值范围、存储方式以及允许进行的运算。可以认为,数据类型是在程序设计中已经实现了的数据结构。另一方面,在程序设计过程中,当需要引入某种新的数据结构时,总是借助编程语言所提供的数据类型来描述数据的存储结构。
    计算机中表示数据的最小单位是二进制数的一位,叫做位。我们用一个由若干位组合起来形成的一个位串表示一个数据元素,通常称这个位串为元素或结点。当数据元素由若干数据项组成时,位串中对应于各个数据项的子位串称为数据域。元素或结点可看成是数据元素在计算机中的映象。
    一个软件系统框架应建立在数据之上,而不是建立在操作之上。一个含抽象数据类型的软件模块应包含定义、表示、实现三个部分。
    对每一个数据结构而言,必定存在与它密切相关的一组操作。若操作的种类和数目不同,即使逻辑结构相同,数据结构能起的作用也不同。
    不同的数据结构其操作集不同,但下列操作必不可缺:
    1,结构的生成;
    2.结构的销毁;
    3,在结构中查找满足规定条件的数据元素;
    4,在结构中插入新的数据元素;
    5,删除结构中已经存在的数据元素;
    6,遍历。
    抽象数据类型:一个数学模型以及定义在该模型上的一组操作。抽象数据类型实际上就是对该数据结构的定义。因为它定义了一个数据的逻辑结构以及在此结构上的一组算法。抽象数据类型可用以下三元组表示:(D,S,P)。D是数据对象,S是D上的关系集,P是对D的基本操作集。ADT的定义为:
    ADT 抽象数据类型名:{数据对象:(数据元素集合),数据关系:(数据关系二元组结合),基本操作:(操作函数的罗列)}; ADT抽象数据类型名;抽象数据类型有两个重要特性:
    用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。
    将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节。
    数据(Data)是信息的载体,它能够被计算机识别、存储和加工处理。它是计算机程序加工的原料,应用程序处理各种各样的数据。计算机科学中,所谓数据就是计算机加工处理的对象,它可以是数值数据,也可以是非数值数据。数值数据是一些整数、实数或复数,主要用于工程计算、科学计算和商务处理等;非数值数据包括字符、文字、图形、图像、语音等。数据元素(Data Element)是数据的基本单位。在不同的条件下,数据元素又可称为元素、结点、顶点、记录等。例如,学生信息检索系统中学生信息表中的一个记录等,都被称为一个数据元素。
    有时,一个数据元素可由若干个数据项(Data Item)组成,例如,学籍管理系统中学生信息表的每一个数据元素就是一个学生记录。它包括学生的学号、姓名、性别、籍贯、出生年月、成绩等数据项。这些数据项可以分为两种:一种叫做初等项,如学生的性别、籍贯等,这些数据项是在数据处理时不能再分割的最小单位;另一种叫做组合项,如学生的成绩,它可以再划分为数学、物理、化学等更小的项。通常,在解决实际应用问题时是把每个学生记录当作一个基本单位进行访问和处理的。
    数据对象(Data Object)或数据元素类(Data Element Class)是具有相同性质的数据元素的集合。在某个具体问题中,数据元素都具有相同的性质(元素值不一定相等),属于同一数据对象(数据元素类),数据元素是数据元素类的一个实例。例如,在交通咨询系统的交通网中,所有的顶点是一个数据元素类,顶点A和顶点B各自代表一个城市,是该数据元素类中的两个实例,其数据元素的值分别为A和B。 数据结构(Data Structure)是指互相之间存在着一种或多种关系的数据元素的集合。在任何问题中,数据元素之间都不会是孤立的,在它们之间都存在着这样或那样的关系,这种数据元素之间的关系称为结构。

    数据结构常用结构

    数据结构数组

    在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。在C语言中, 数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。

    数据结构栈

    是只能在某一端插入和删除的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。

    数据结构队列

    一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列是按照“先进先出”或“后进后出”的原则组织数据的。队列中没有元素时,称为空队列。

    数据结构链表

    是一种物理存储单元上非连续、非顺序的存储结构,它既可以表示线性结构,也可以用于表示非线性结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

    数据结构树

    是包含n(n>0)个结点的有穷集合K,且在K中定义了一个关系N,N满足 以下条件:
    (1)有且仅有一个结点 K0,他对于关系N来说没有前驱,称K0为树的根结点。简称为根(root)。 
    (2)除K0外,K中的每个结点,对于关系N来说有且仅有一个前驱。
    (3)K中各结点,对关系N来说可以有m个后继(m>=0)。

    数据结构图

    图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。

    数据结构堆

    在计算机科学中,堆是一种特殊的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。

    数据结构散列表

    若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个思想建立的表为散列表
  • 相关阅读:
    C++多态性(静多态和动多态)
    根据二叉树的后序遍历以及中序遍历还原二叉树
    哈希表
    *********** 复习算法复杂度,基础 ************
    C++虚函数表解析
    为什么需要auto_ptr_ref
    python未修辞的计时器
    python删除一个非空文件夹竟然如此简单
    python模拟163登陆获取邮件列表
    wxpython制作eml文件阅读器
  • 原文地址:https://www.cnblogs.com/WuXuanKun/p/5549626.html
Copyright © 2020-2023  润新知