数据结构实现基础

Posted 天“码”行空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构实现基础相关的知识,希望对你有一定的参考价值。

什么是数据结构?

笔者认为数据结构包括三个部分【1】数据的逻辑结构,即数据对象集【2】数据的物理存储结构,即数据对象集在计算机中的组织方式【3】数据对象集相关联的操作集,以及实现这些操作集的最高效的算法。简而言之,数据结构包含数据逻辑结构,数据物理存储结构,算法。

数据结构往往是抽象的,它的实现需要依赖具体的程序设计语言,一般使用C语言来实现。

例1 在日常数据处理中经常碰到的问题是需要对一组数据进行基本的统计分析。比如分析一个班级学生的平均成绩,最高成绩,最低成绩,中位数等。再比如,统计家庭每年每月的开支情况,生产线上各位员工计件任务的完成情况,各省的人均GDP数据等。为每个具体应用都编一个程序显然不是好的方法,因为这些程序有很大的相似性。数据结构的处理方法是从这些具体应用中抽象出共性的数据组织和操作方法,进而采取某种具体的程序设计语言实现相应的数据存储和操作。比如对上面的例子我们可以从不同的应用背景中抽象出一种针对基本统计要求的数据类型(抽象数据类型ADT)。

类型名称:统计数据集

数据对象集:N个元素x1,x2,x3,.......xN的集合S。

操作集:

1.ElementType Average(S,N):求S中N个元素的平均值。

2.ElementType Max(S,N):求S中N个元素的最大值。

3.ElementType Min(S,N):求S中N个元素的最小值。

4.ElementType Median(S,N):求S中N个元素的中位数。

可以看到,针对上面的数据抽象方式的具体程序,可以用来求解不同领域的基本统计问题,这样使得我们的程序设计逻辑清晰,实现代码的重用。

如何利用程序设计语言实现上述抽象数据类型(ADT)呢?首先,必须考虑数据如何存储-----在我们这个例子中,即考虑集合S的数据在C语言中如何存储;其次必须考虑操作如何实现,即在确定好数据存储方式的基础上,相应的操作(如Average函数,Max函数)如何实现?

一.数据存储

上述问题,数据对象集是集合S,这也是最简单的一种数据组织方式。今后我们还会学习到一些复杂的数据组织方式,比如树,图。(集合也是一种数据组织方式,其不要求数据有序存放)(数据组织方式)

C语言提供数据组织的几种基本实现方式,包括数组,链表,结构体等。事实上,数据组织的基本存储方式主要是利用数组和链表来实现的,包括很复杂的数据结构(图,树)等都是应用数组和链表来实现。(数据存储实现方式)

在例1中,可以简单地使用数组来存储一个集合。这样相关的操作就可以在数组上进行了。

注:数据结构的存储实现跟所需操作密切相关,没有最好的存储方式,只有最合适的存储方式。

二.操作实现

在确定数据的存储方式后,数据结构涉及的另外一个问题是相关的操作(运算)如何实现。这些操作的实现需要利用的是程序设计语言提供的另外一个功能,即流程设计功能。

任何程序设计语言都有分支控制语句,循环控制语句,顺序执行语句,是算法流程的基本结构。函数构成的模块化使得程序便于设计,调试和维护。

回到例1的基本统计问题,就操作集中列举的四种操作而言,如果数据存储在一个数组S中,显然相关的Average(S,N),Max(S,N),Min(S,N)操作都可以很容易地用循环实现。如下代码展示了Average函数实现:

ElementType Average(ElementType S[], int N)
//求N个集合元素S[]的平均值
   int i;
   ElementType Sum = 0;
 
   for(i=0; i<N; i++)
       sum += S[i];
   return Sum/N;

而对于求中位数Median(S,N)问题则比较复杂,没法用一个循环简单实现。要解决中位数的问题,我们先来看下列问题:

例2:现有一个集合6 5 9 8 2 1 7 3 4,求其中第五大的数?

【1】拿到这样的问题,我相信大多数人的想法应该是先将集合中的元素按从大到小(为什么是从大到小呢?结合问题思考一下)的顺序排列,然后利用数组来实现存储,并且通过数组下标调用来找到其中第五个元素。而这个数恰好就是第五大的数。 我们把这种方法叫做基于排序的方法。

【2】除此之外,在这里我们还介绍一种方法,这种方法是基于问题分解的,所以叫做基于问题分解法。

解:该集合有9个元素,我们要取出其中第5大的元素。利用问题分解的思路可以解决,先取出集合的第1个元素6,根据这个元素分解出大于6或等于6的其他元素。S1=9 8 7, S2=5 2 1 3 4。

由于|S1|(比所取元素大的元素组成的集合元素个数)= 3,说明比6大的元素只有3个,意味着6是集合中第四大的数,也进一步推出第五大的数一定在集合S2中,并且是S2中第一(5-3-1=1)大的数。现在我们就是要求S2中第一大的数,利用刚才同样的解决方法将问题分解,这样选取S2中第1个元素5,比5大的元素构成集合S3=,比5小的元素构成集合S4=2 1 3 4。

|S3|=0,所以所选取的元素5恰好就是集合S2中最大的元素。也就是集合6 5 9 8 2 1 7 3 4中第五大的数

上述求集合中第几大的数的方法是一种将大问题分解成小问题的求解方法。由于小问题的求解采用于大问题相同的思路,故可以采用函数递归的方法实现。

ElementType FindKthlargest(ElementType S[], int K)
//用于求第K大的数的算法的伪代码
  选取S中第一个元素e;
  根据e将集合(不包含e)分解为大于等于e的元素集合S1和小于e的元素集合S2;
  if(|S1|>=K) 
    return FindKthlargest(S1[], K);
  else if(|S1|<K-1)
    return FindKthlargest(S2[], K-|S1|-1);
  else
    return e;

例2的伪代码

int FindKthlargest(int S[], K)

  选取第一个元素6;
  S1 = 9 8 7;
  S2 = 5 2 1 3 4;
  |S1| == 3 < 5-1
  故第K个数一定在S2中,且是S2中第1大的数
  Find5thlargest(S2[], 1)
  选取S2中第一个元素5;
  S3 = ;
  S4 = 2 1 3 4;
  |S3| == 0 =1-1
  return e(最新的e为5);

  

总结求集合中第几大数的两种方法:

方法1:基于排序法

首先将集合S中的元素从大到小排序,然后取第几处的元素就是第几大的元素。

方法2:基于问题分解

用一个基准数e将集合S分解为不包含e在内的两个小集合S1和S2,其中S1的任何元素均大于e,S2的任何元素均小于e。记|S|代表集合S元素的个数,这样如果|S1|>=K,则说明第K大的数在S1中;如果|S1|正好等于K-1,说明e是第K大的数;否则第K大的数在S2中,并且是S2中第K-|S1|-1大数。然后用类似的思路在S1或S2中查找。因为用类似的方法在子集合中查找,所以可以设计成递归函数。递归函数设计的本质就是小问题与大问题有同样的思路。

扩展:明白了如何求一个集合中第几大的数之后,现在如果要你求中位数Median(S,N)问题,你还会求吗?

我们只需要抓住一点就是:一个集合有N个元素,这N个元素的中位数是N个元素从大到小的顺序排序后取其中的第【N/2】(大于或等于N/2的最小整数)处的元素就是中位数。

总结:这一小节,我们主要学习了数据存储和操作实现,以及如何求集合中第几大的数的问题分解法。

以上是关于数据结构实现基础的主要内容,如果未能解决你的问题,请参考以下文章

mysql小节

平安面试小节

基础乐理

数据结构二叉树相关面试题 Java版 LeetCode题 ------- 二叉树

第4节:Java基础 - 必知必会(中)

基础乐理