数据结构-线性表之顺序表_java今天正式开工!开始总结数据结构这部分知识.

    

数据结构-线性表之顺序表_java_02


    在正式总结前,我们梳理下数据结构的内容,算是绪论内容吧。


00


知识框架



数据结构-线性表之顺序表_java_03

在数据结构三要素中,我们主要梳理下数据的逻辑结构和物理结构。

【数据逻辑结构】


数据结构-线性表之顺序表_java_04

    逻辑结构是指数据元素之间的逻辑关系,即从逻辑关系上描述数据。它与数据的存储无关,是独立于计算机的。常见的数据逻辑结构分类如上图1.1。
    集合   结构中的数据元素之间除了“同属于一个集合”的关系外,别无其他关系。
    线性结构  结构中的数据元素之间只存在一对一的关系。
    树形结构  结构中的数据元素之间存在一对多的关系。
    图状结构或网状结构  结构中的数据元素之间存在多对多的关系。

【数据的存储结构】

    存储结构是指数据结构在计算机中的表示(又称映像),也称物理结构。它包括数据元素的表示和关系的表示。数据的存储结构是逻辑结构用计算机语言的实现,它依赖于计算机语言。数据的存储结构主要有:顺序存储、链式存储、索引存储和散列存储。关于这些存储结构的定义请大家自行搜索理解。


【ADT】

    抽象数据类型(ADT)是指一个数学模型以及定义在该模型上的一组操作,通常用(数据对象、数据关系、基本操作集)这样的三元组来表示抽象数据类型。因此可以用ADT定义一个完整的数据结构。


数据结构-线性表之顺序表_java_05




线性表



    【知识框架】

    

数据结构-线性表之顺序表_java_06


01


理论知识


【线性表的定义】

数据结构-线性表之顺序表_java_07


【线性表的基本操作】

    线性表的主要操作有:

数据结构-线性表之顺序表_java_08


【顺序表的定义】

    线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。第一个元素存储在线性表的起始位置,第i个元素的存储位置后面紧接着存储的是第i+1元素,称i为元素数据结构-线性表之顺序表_java_09在线性表中的位序。因此,顺序表的特点是表中元素的逻辑顺序与其物理顺序相同。

    

数据结构-线性表之顺序表_java_10

    注意:线性表中元素的位序是从1开始的,而数组中元素的下表是从0开始

    假定线性表的元素类型为ElemType,则线性表的顺序存储类型使用C语言描述为

#define MaxSize 50  //定义线性表的最大长度typedef struct{     ElemType data[MaxSize]; //顺序表的元素    int length; //顺序表的当前长度}SqList;     //顺序表的类型定义

    一维位数组可以是静态分配的,也可以是动态分配的,在静态分配中由于数组大小和空间已定,一旦空间占满,再加入新的数据将会产生溢出,进而导致程序崩溃。

    而在动态分配中,存储数组的空间是在程序执行过程中通过动态分配语句分配的,一旦数据空间占满,就另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要为线性表一次性的划分所有空间。

#define InitSize 100 //表长度的初始定义typedef struct{    ElemType *data;   //指示动态分配数组的指针    int MaxSize,length;  //数组的最大容量和当前个数 } SeqList;       //动态分配数组顺序表的类型定义

    C的初始动态分配语句为

L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);

    C++的初始动态分配语句为

L.data = new ElemType[InitSize];

数据结构-线性表之顺序表_java_11

顺序表最主要的特点是 随机访问,即通过首地址和元素序号可在时间O(1)内找到指定的元素。顺序表的存储密度高,每个结点只存储数据元素。顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量元素。


02


编程实现


    考虑到自己日后以Go语言为主要工作语言,因此我会将所有的数据结构以Go语言改写,当然C/C++如何实现也是需要掌握的,理论知识暂且以C/C++进行总结。

package main
import "fmt"
// SqList  is a sqlist file//定义顺序表type SqList struct{  //定义顺序表的最大长度  maxSize int  //定义当前顺序表的长度  length int  //顺序表的元素  data []int}
//InitSqList is to initmylist//初始化顺序表func InitSqList(InitSize int) *SqList{  return &SqList{maxSize: InitSize, data: make([]int, InitSize)}}
//IsEmpty is or not empty//检查顺表是否为空//方法是查看当前顺序表的长度是否为0 func (mySqList *SqList) IsEmpty() bool{  return mySqList.length == 0}
//IsFull is or not full//判断当前顺序表长度是否已达到最大值func (mySqList *SqList) IsFull() bool{  return mySqList.length == mySqList.maxSize}
//ListInsert function is to insert an element//在i位置插入元素efunc (mySqList *SqList) ListInsert(i, e int) bool{  if i < 1 || i > mySqList.length + 1 {    fmt.Println("不合法,请检查i")    return false  }  if i > mySqList.maxSize {    fmt.Println("不合法,请检查i")    return false  }  for j := mySqList.length; j >= i; j--{  //将第i个元素及之后的元素后移    mySqList.data[j] = mySqList.data[j-1]  }  mySqList.data[i-1] = e    //在位置i处放入e  mySqList.length++        //线性表长度加1  return true}
//ListAppend function is to append an element//在尾部追加一个元素,需要和最大长度比较func (mySqList *SqList) ListAppend(e int) bool{  if mySqList.IsFull() {    fmt.Println("sorry list is maxsize")    return false  }  mySqList.data[mySqList.length] = e  mySqList.length++  return true}
//ListDelete function is to remove en element//此方法是用来删除指定位置元素 //method 1func (mySqList *SqList) ListDelete(i int) bool{  if i < 1 || i > mySqList.length + 1 {    fmt.Println("i不合法,请检查i")    return false  }  if i > mySqList.maxSize {    fmt.Println("i不合法,请检查i")  }  for j := i-1; j < mySqList.length-1; j++{  //将第i个位置之后的元素前移    mySqList.data[j] = mySqList.data[j+1]  }  mySqList.data[mySqList.length-1] = 0  mySqList.length--  return true }
//ListDelete function is to remove element//method2// func (mySqList *SqList) ListDelete(i int) bool{//   if i < 1 || i > mySqList.length + 1 {//     fmt.Println("i不合法,请检查i")//     return false//   }//   if i > mySqList.maxSize {//     fmt.Println("i不合法,请检查i")//   }//区别主要在这里,有两种思路//   for( j := i; j < mySqList.length; j++){//     mySqList.data[j-1] = mySqList.data[j]//   }//   mySqList.length--//   return true// }
//ListFind function is to find an element//此方法用来查找指定位置的元素func (mySqList *SqList) ListFind(i int) bool{  if i < 1 || i > mySqList.length + 1 {    fmt.Println("i不合法,请检查i")    return false  }  if i > mySqList.maxSize {    fmt.Println("i不合法,请检查i")    return false  }  fmt.Println(mySqList.data[i-1])  return true}func main(){  list := InitSqList(20)  //设置最大长度为20  list.ListInsert(1,22)  list.ListAppend(12)  list.ListAppend(122)  list.ListAppend(133)  list.ListAppend(143)  list.ListAppend(153)  list.ListAppend(173)  list.ListAppend(183)  list.ListAppend(193)  list.ListAppend(163)  fmt.Println(&list)  fmt.Println(*list)  fmt.Println(*&list.length)  list.ListDelete(5)  //fmt.Println(*&list.data[2])  fmt.Println(*list)  list.ListFind(3)}

    程序执行结果是

数据结构-线性表之顺序表_java_12


注意:区别顺序表的位序和数组下标。理解为什么判断插入位置是否合法时if语句中用length+1,而移动元素的for语句中只用length?



03


算法复杂度分析


【插入操作】

最好情况:在表尾插入(即i=n+1)元素后移语句将不执行,时间复杂度为O(1).

最坏情况:在表头插入(即i=1),元素后移语句将执行n次,时间复杂度为O(n).

平均情况:假设数据结构-线性表之顺序表_java_13是在第i个位置上插入一个结点的概率,则在长度为n的线性表中插入一个结点时所需移动结点的平均次数为

数据结构-线性表之顺序表_java_14

因此,线性表插入算法的平均时间复杂度为O(n).


【删除操作】

最好情况:删除表尾元素(即i=n)无需移动元素,时间复杂度为O(1).

最坏情况:删除表头元素(即i=1)需要移动除第一个元素外的所有元素,时间复杂度为O(n).

平均情况:假设数据结构-线性表之顺序表_java_15是删除第i个位置上结点的概率,则在长度为n的线性表中删除一个结点时所需移动结点的平均次数为

数据结构-线性表之顺序表_java_16

因此,线性表删除算法的平均时间复杂度为O(n).


【按值查找】顺序查找

最好情况:查找的元素就在表头,仅需比较一次,时间复杂度为O(1).

最坏情况:查找的元素在表尾(或不存在)时候,需要比较n次,时间复杂度为O(n).

平均情况:假设数据结构-线性表之顺序表_java_17是查找的元素在第i(1<=i<=L.length)个位置上的概率,则在长度为n的线性表中查找值为e的元素所需比较的平均次数为

数据结构-线性表之顺序表_java_18

因此,线性表按值查找算法的平均时间复杂度为O(n).


end


说明:考虑到时间成本,有些理论知识截图借鉴了部分计算机专业教材。还有考虑到一些数学符号问题,会逐渐使用LaTex命令进行排版。另外,由于每个部分知识点较多,故可能不会日更数据结构-线性表之顺序表_java_19