软件程序员今日看点

数据结构三(线性表)

2016-09-18  本文已影响343人  e40c669177be

1.线性表的定义

线性表:零个多个数据元素的有限序列
序列:也就是说元素之间是有顺序的,若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他的每个元素都有且只有一个前驱后继
定义:
若将线性表记为(a1, a2......ai-1,ai,ai+ 1,.....an),则表中ai-1领先于ai,ai领先于ai+ 1,称ai -1 为ai的前驱,ai + 1为ai的后继.当i = 1, 2....n- 1,ai有且只有一个直接后继,当i = 2,3,.....n时,ai有且只有一个直接前驱


线性表元素的个数n(n >= 0)定义为线性表的长度,当n= 0时,成为空表.

在较复杂的线性表中,一个数据元素可以由若干个数据项组成.

2.线性表的抽象数据类型

ADT 线性表(list)
Data 线性表的数据对象集合为(a1,a2,....an),每个元素的类型均为DataType
Operation
InitList(L):初始化操作,建立一个空的线性表L
ListEmpty(L):若线性表为空,返回true,否则返回false
ClearList(
L):将线性表清空
GetElem(L,i,e):将线性表L中的第i个位置元素值返回给e
LocateElem(L,e):线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功,否则,返回0表示失败
ListInsert(
L.i.e):在线性表L中的第i个元素插入新元素e
ListDelete(L,u,e):删除线性表L中的第i个元素,并用e返回其值
ListLength(L):返回线性表L的元素个数
endADT

实现两个线性集合A和B的并集操作,即A = A U B,就是把集合B中但并不存在A中的数据元素插入到A中即可.
操作:

3.线性表的顺序存储结构

线性表的两种物理结构的第一种----顺序存储结构
线性表的顺序存储结构:指的是用一段地址连续的存储单元依次存储线性表的数据元素

顺序存储结构示意图
#define MAXSIZE 20;//存储空间初始分配量
typedef int ElemType; //ElemType类型根据实际情况而定,这里假设为int 
  typeded struct{
  ElemType data[MAXSIZE];//数组存储数据元素,最大值为MAXSIZE
 int length;//线性表当前元素
}Sqlist;

顺序存储结构需要三个属性:

3.1.数据长度与线性表长度的区别

数据长度:是存放线性表的存储空间的长度,存储分配后这个量是一般不变的
线性表的长度:线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是变化的
在任意时刻,线性表的长度应该小于等于数组的长度

3.2.地址计算方法

LOC(ai) = LOC(a1) + (i - 1) *c
c是占用存储单元
线性表的起始为1
C语言中数组是从0开始的


地址计算

他的存储时间性能为O(1),我们通常把这一特点的存储结构为随机存储结构

3.3.顺序存储结构的插入与删除

3.3.1获取元素操作

只要i的数组在数组下标范围内,就把数组第i- 1下标的值返回即可

#define OK 0
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
//Status 是函数的类型,其值是函数结果状态代码.如OK等
//初始条件:顺序线性表L已存储,1<= i <= LIstLength (L)
//操作结果:用e返回L中第i个数据元素的值
Status GetElem (Sqlist L,int i ,ElemType *e){
  if (L.length == 0 || i < 1 || i > L.length)
  return ERROR
  *e = L.data(i - 1);
  return OK;
 }

3.3.2插入操作

** 插入算法的思路:**

3.3.3删除操作

删除操作思路

最好情况:插入和删除的都在最后一个元素,此时时间复杂度为 O(1)
最坏情况:插入和删除的都是第一个元素,移动所有的元素,时间复杂度为O(n)
平均情况:次数为(n- 1)/ 2,时间复杂度还是O(n)

3.3.5线性存储结构的优缺点

3.4线性表的链式存储结构

为了解决线性表的顺序存储结构,插入数据需要移动大量的元素,而存在的线性表链式存储结构
定义: 为了表示每个数据元素ai与其直接后继数据元素ai+ 1之间的逻辑关系.对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其后继的信息(即直接后继的存储位置).我们把存储数据元素信息的域称为数据域,把存储后继的位置的域称为指针域.指针域中存储的信息称做指针.这两部分信息组成的元素ai的存储映像,称为结点
n个结点(ai的存储映像)链结成一个链表,即为线性表(a1,a2,...an)的链式存储结构,因为此链表的每个结点中只包含一个指针域,所有叫做单链表

单链表

单链表通过每个结点的指针域将线性表的数据元素按其逻辑顺序依次链接在一起.

链表中第一个结点的存储位置叫做头指针,整个链表的存储必须是从头指针开始进行的,之后的每一个结点,其实就是上一个的后继指针指向的位置.最后一个,其后继不存在,我们规定,线性链表的最后一个结点指针为''空"(通常用NULL或"^"表示)

单链表的结构

为了更方便的进行指针的操作,我们会在单链表的第一个结点附近设置一个结点,称为头结点.头结点的数据域可以不存储任何信息,也可以存储线性表的程度长度等附加信息,头结点的指针域指向第一个结点的指针

屏幕快照 2016-09-18 下午1.37.47.png

3.5头指针与头结点的异同

头指针:
1)头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针
2)头指针具有标识作用,所以常常用头指针冠宇链表的名字
3)无聊链表是否为空,头指针均不为空.头指针是链表的必要元素
头结点:
1)头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可存放链表的长度)
2)有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了
3)头结点不一定是链表必须要素

3.6线性表链式存储结构描述

1.线性表为空

空链表 单链表 带有头结点的单链接 空的单链表
//线性表的单链表存储结构
typedef struct Node{
 ElemType data;
 struct Node *next;
}Node;
typedef struct Node *LinkList;//定义LinkList 

由这个结构的定义中,我们知道,结点由存放数据元素的数据域,存放后继结点的指针的指针域组成
假设p是指向线性表第i个元素的指针,则
该结点ai的数据域用p->data来表示
p->data的值是一个数据元素
结点ai的指针域可以用p->next表示,
p->next的值是一个指针
p->next指向第i+1个元素,即指向ai+1的指针.也就是说 p->data = ai;
p->next ->data = ai+ 1;

屏幕快照 2016-09-18 下午2.04.21.png

3.7单链表的读取

获取链表第i个数据的算法思路

单链表的最坏时间复杂度是O(n)
因为单链表的结构定义中没有定义表长,所以实现不知道循环多少次,因此不方便使用for来循环控制,其主要核心思想是"工作指针后移"

3.7单链表的插入与删除

3.71单链表的插入

单链表的插入

只需要让s->next = p->next,p->next = s即可

插入 插入后
单链表第i个数据插入节点的算法思想

3.72单链表的删除

单链表的删除

实际上就一步 ,p->next = p ->next -> next,用q 来取代p ->next即

q = p -> next, p -> next = q -> next;

单链表第i个数据删除结点的算法思路

3.73单链表的创建

思路:

我们也可以把每次新结点都插在终端结点的后面,我们称为尾插法

//随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法)

void  CreateListTail(LinkList *L , int n){
      LinkList p,r;
      int i;
      srand(time(0));
      *L = (LinkList)malloc(sizeof(Node));
      r = *L;//r为指向尾部的结点
      for (i = 0 ; i < n; i++){
            p = (Node *)malloc(sizeof(Node));
           p ->data = rang() % 100 + 1;
            r -> next = p;//将表尾终端结点的指针指向新结点
            r = p;//将当前的新结点定义为表尾终端指针
          }
      r -> next = NULL;//表示当前链表结束
    }

3.74单链表的整表删除

单链表的整表删除思路:

3.8单链表结构与顺序存储结构优缺点

存储分配方式:
1)顺序存储结构用一段连续的存储单元移除存储线性表的数据元素
2)单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素
时间性能
1)查找

3.9循环链表

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链接,简称循环链表

空链表
非空的循环链表

与单链表的主要差异就是在循环判断条件上,原来判断p-> next 是否为空,现在则是p->next不等于头结点,循环未结束

用O(1)的时间由链表指针访问最后一个结点.我们需要改造下这个循环链表,不用头指针,而是用指向终端结点的尾指针来表示循环链表,这样查找开始结点和终端结点就很方便了
终端结点用尾指针rear指示.开始结点:rear ->next -> next,时间复杂度也是O(1)
链表的合并

链表的合并
p = rearA  ->next;//保存A表的头结点,即1
rearA ->next = rearB -> next ->next;//将本是指向B表的第一个结点(不是头结点)赋值给reaA -> next
rearB -> next = p;//将原A表的头结点赋值给rearB
free(p);

3.10双向链表

双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域

双向链表的存储结构
typedef struct DulNode{
    ElemType data;
    struct DulNode *prior;//直接前驱指针
    struct DulNode *next;..直接后继指针
    }DulNode , *DuLinkList;
双向链表的空链表 ![Uploading 屏幕快照 2016-09-18 下午4.45.46_357213.png . . .] 非空循环的双向链表

双向链表他的后继的前驱是他自己,他的前驱的后继也是他自己

p -> next -> prior = p = p -> prior -> next

3.10.1双向链表的插入

插入
s -> prior = p ;//把p赋值给s的前驱,如图中1
s ->next = p -> next;//把p->next赋值给s的后继,如图中2
p-> next -> prior = s;//把s赋值给p->next的前驱,如图3
p -> next = s;//把s赋值给p的后继,如图4

3.10.2双向链表的删除

删除
p -> prior -> next = p -> next;
p -> next -> prior = p -> prior;
free(p);

总结

线性表
上一篇下一篇

猜你喜欢

热点阅读