数据结构之线性结构

合集下载

数据结构之线性结构和非线性结构

数据结构之线性结构和非线性结构

数据结构之线性结构和⾮线性结构线性结构:⼀、概念1. 线性结构作为最常⽤的数据结构,其特点是数据元素之间存在⼀对⼀的线性关系。

2. 线性结构拥有两种不同的存储结构,即顺序存储结构和链式存储结构。

顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的,链式存储的线性表称为链表,链表中的存储元素不⼀定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。

3. 线性结构中存在两种操作受限的使⽤场景,即队列和栈。

栈的操作只能在线性表的⼀端进⾏,就是我们常说的先进后出(FILO),队列的插⼊操作在线性表的⼀端进⾏⽽其他操作在线性表的另⼀端进⾏,先进先出(FIFO),由于线性结构存在两种存储结构,因此队列和栈各存在两个实现⽅式。

⼆、部分实现1. 顺序表(顺序存储) 按照我们的习惯,存放东西时,⼀般是找⼀块空间,然后将需要存放的东西依次摆放,这就是顺序存储。

计算机中的顺序存储是指在内存中⽤⼀块地址连续的空间依次存放数据元素,⽤这种⽅式存储的线性表叫顺序表其特点是表中相邻的数据元素在内存中存储位置也相邻,如下图:1 // 倒置线性表2 public void Reverse()3 {4 T tmp = default(T);56 int len = GetLength() - 1;7 for (int i = 0; i <= len / 2; i++)8 {9 if (i.Equals(len - i))10 {11 break;12 }1314 tmp = data[i];15 data[i] = data[len - i];16 data[len - i] = tmp;17 }18 }2. 链表(链式存储) 假如我们现在要存放⼀些物品,但是没有⾜够⼤的空间将所有的物品⼀次性放下(电脑中使⽤链式存储不是因为内存不够先事先说明⼀下...,具体原因后续会说到),同时设定我们因为脑容量很⼩,为了节省空间,只能记住⼀件物品位置。

《数据结构》线性结构实验报告

《数据结构》线性结构实验报告

《数据结构》线性结构实验报告2、源程序:#include <stdio.h>#include<stdlib.h>#define MAXSIZE 1024typedef int elemtype;typedef struct SequenStack{elemtype data[MAXSIZE];int top;}SequenStack;SequenStack * Init_SequenStack(){SequenStack * S;S = (SequenStack *)malloc(sizeof(SequenStack));if (S == NULL)return S;S->top = -1;return S;}int SequenStack_Empty(SequenStack * S)//判栈空{if (S->top == -1){return 1;}{int a;printf("请以十进制输入一个数:\n");scanf_s("%d", &a);printf("转化为二进制为:");Conversion(a);printf("\n");}运行结果:3、源程序:#include<stdio.h>#include<stdlib.h>#include<string.h>typedef struct node{char data;struct node* next;}LinkStack;//初始化LinkStack* Init_LinkStack(){LinkStack* top;top = (LinkStack*)malloc(sizeof(LinkStack));top->next = NULL;return top;}//入栈void Push_LinkStack(LinkStack* top, char x){LinkStack* node;node = (LinkStack*)malloc(sizeof(LinkStack));node->data = x;node->next = top->next;top->next = node;}运行结果:4、源程序:#include <stdio.h>#include<stdlib.h>#include<string.h>#define MAXSIZE 20typedef int elemtype;typedef struct QueueNode{elemtype data;struct QueueNode* next;}LinkedQueueNode;typedef struct LQueue{LinkedQueueNode* front;LinkedQueueNode* rear;}LQueue, *LinkedQueue;typedef struct Person{char name[MAXSIZE];char sex;}Person;typedef char* ElemType;//链队初始化LinkedQueue Init_LinkedQueue(){LinkedQueue Q = (LinkedQueue)malloc(sizeof(LQueue));LinkedQueueNode * head = (LinkedQueueNode *)malloc(sizeof(LinkedQueueNode));if (head != NULL && Q != NULL){head->next = NULL;Q->front = head;Q->rear = head;printf("输入参与者的姓名,性别\n");for (i = 0; i < num; i++){printf("输入第%d个舞者的名字:\n", i + 1);scanf_s("%s", &dancer[i].name, 10);printf("输入第%d个人的性别(F/M):\n", i + 1);scanf_s("%s", &dancer[i].sex, 10);while (dancer[i].sex != 'F' && dancer[i].sex != 'M'){printf("输入错误,请重新输入第%d个人的性别(F/M):\n", i + 1);scanf_s("%s", &dancer[i].sex, 10);}}DancePartner(dancer, num);break;case 0:printf("感谢你的使用!\n");break;default:printf("无此选项!\n");break;}} while (n != 0);return 0;}运行结果:。

线性数据结构例题分析

线性数据结构例题分析

算法分析(朴素的算法): 对于每个大小为k的区间,都要计算最大值和最小值 时间复杂度:O(n * k)
位置 数值
1 1
2 3
3 -1 -1
4 -3 -3 -3
5 5 -3 5
6 3 -3 3
7 6 3 6
8 7 3 7
min
队列: 1 3
-1
观察队列中元素离开队列的情况:
1.元素Vi从队尾离开队列:
第i+1个元素以及它后面的元素依次前移。
其中,2、3操作中都可能需要移动大量元素。
总复杂度:O(n * m)。
Байду номын сангаас 5 1 2 2 2 7 1 2 3 3 6 31 23 14 5 7
1 3
链表: 元素数组:data[maxn]; 指针数组:next[maxn]; //元素data[i]的后继元素所在位置是next[i] 头结点指针:head 不要求逻辑上相邻的元素存储位置也相邻,不能随机存储。 5 1 2 2 2 7 1 2 3 3 1 3
算法分析: 维护三个数组q1,q2,q3; 取q2、q3队首元素的较小者k,加入q1,相应队列的队首位置后移, 2*k+1、3*k+1分别加入q2、q3;
直到q1中的元素个数达到n个。
实际上,q2、q3中的元素都来自于q1,只要维护two、three两个位置,
表示q2中的下一个数由q1[two]*2+1得到,q3中的下一个数由
j插在i的右侧:
ll[j] = i; rr[j] = rr[i];
ll[rr[i]] = j; rr[i] = j;
删除i: rr[ll[i]] = rr[i]; ll[rr[i]] = ll[i];

数据结构 线性表

数据结构 线性表

(9) Status NextElem_Sq(SqList L, ElemType cur_e, ElemaType &next_e)
//若cur_e是线性表L的元素且不是最后一个,返回它的后继 { for (i=0; i<L.length-1; i++) if (cur_e==L.elem[i]) { next_e=L.elem[i+1]; return OK; } return ERROR; }//NextElem_Sq O(n)
抽象数据类型 唯 一 数据的逻辑结构 确 操作的定义 定
集合 *
线性表
特殊线性表 扩展线性表
线性结构
树形结构 图形结构
灵 活 数据的存储结构 操作的实现 设 计
顺序存储 链式存储 散列(哈希)存储
数据的基本操作:针对结构、针对元素、针对状态
数据结构---第二章 线性表 1
第二章 线性表
2.1 2.2 2.3 2.4
数据结构---第二章 线性表
9
2.2 线性表的顺序存储结构(顺序表)
起始地址为b、最多可容纳maxlen个元素的线性表
下标 存储地址
0
1
b b+c
b+(i-1)c
a1 a2
ai
c个存储单元
i-1
LOC(ai)=LOC(a1)+(i-1)c LOC(ai)=LOC(ai-1)+c
n-1
b+(n-1)c
n-1
int LocateElem_Sq(SqList L, ElemType e, (7) Status (*compare)(ElemType,ElemType) ) //在线性表L中查找第1个值与e满足 //compare()的元素的位序 { for (i=0; i<L.length; i++) L.elem[i]==e if ( (*compare)(L.elem[i],e) ) return i+1; return 0 ; //作为未找到的特殊标记 } // LocateElem_Sq O(n) P25-2.6

数据结构-线性结构

数据结构-线性结构

数据结构-线性结构线性表线性表是最简单最常见的数据结构,属于逻辑结构;线性表有两种实现⽅式(存储⽅式),分别是顺序实现和链接实现;定义:线性表是由n(>=0)个数据元素组成的有限序列,数据元素的个数n定义为表的长度;术语:前驱, 后继, 直接前驱, 直接后继, 长度, 空表案例:线性表⽤L表⽰,⼀个⾮空线性表可记为L = (a1,a2,..an);a1后⾯的称为a1的后继an前⾯的称为an的前驱a1为起始节点,an为终端节点,任意相邻的两个元素,如a1和a2,a1是a2的直接前驱,a2是a1的直接后继;线性表中元素个数即表的长度,此处为n;表中没有任何元素时,称为空表除了⾸节点和尾节点之外,每个节点都有且只有⼀个直接前驱和直接后继,⾸节点没有前驱,尾节点没有后继;节点之间的关系属于⼀对⼀;线性表的基本运算初始化Initiate(L) 建⽴⼀个空表L(),L不包含数据元素求表长度Length(L) 返回线性表的长度取表元素Get(L,i) 返回线性表的第i个元素,i不满⾜1<=i<=Length(L)时,返回特殊值;定位Locate(L,x)查找x在L中的节点序号,若有多个匹配的返回第⼀个,若没有匹配的返回0;插⼊Insert(L,x,i)将x插⼊到L的第i个元素的前⾯(其他元素往后挪),参数i取值范围为1<=i<=Length(L)+1;运算结束后表长度+1;删除Delete(L,i)删除表L中的第i个元素,i有效范围1<=i<=Length(L);操作结束后表长度-1强调:上述的第i个指的是元素的序号从1开始,⽽不是下标从0开始;另外:插⼊操作要保证操作后数据还是⼀个接着⼀个的不能出现空缺;线性表的顺序存储实现线性表是⼀种逻辑结构,可以通过顺序存储结构来实现,即:将表中的节点⼀次存放在计算机内存中⼀组连续的存储单元中,数据元素在线性表中的邻接关系决定了它们在存储空间中的存储位置;换句话说逻辑结构中相邻的两个节点的实际存储位置也相邻;⽤顺序存储结构实现的线性表也称之为为顺序表,⼀般采⽤数组来实现;图⽰:⼤⼩与长度:线性表的⼤⼩:指的是最⼤能存储的元素个数线性表的长度:指的是当前已存储的个数⽰例:c语⾔实现:#include <stdio.h>//初始化操作:const MAX_SIZE = 5;//最⼤长度typedef struct list {int data[MAX_SIZE];//数组int length;//当前数据长度};//获取targert在表中的位置int locate(struct list *l,int target){for (int i = 0;i < l->length;i++){if (target == l->data[i]){return i + 1;}}return 0;}//获取第loc个元素int get(struct list *l,int loc){if (loc < 1 || loc > l->length){printf("error:位置超出范围\n");return -1;}else{return l->data[loc-1];}}//插⼊⼀个元素到第loc个位置上void insert(struct list *l,int data,int location){if (l->length == MAX_SIZE){printf("errolr:表容量已满\n");return;}if (location < 1 || location > l->length+1){printf("error:位置超出范围\n");return;}//⽬标位置后⾯的内容以此往后挪for (int i = l->length; i >= location; i--) {l->data[i] = l->data[i-1];}//在⽬标位置放⼊新的数据l->data[location-1] = data;l->length+=1;//长度加1}//删除第loc个元素,从⽬标位置往后的元素⼀次向前移动void delete(struct list *l,int loc){if (loc < 1|| loc > l->length){printf("error:位置超出范围\n");return;}//⽬标位置及后⾯的所有元素全部向后移动for (;loc < l->length; ++loc) {l->data[loc-1] = l->data[loc];}l->length-=1;}//打印所有元素测试⽤void show(struct list l){for (int i = 0; i < l.length; ++i) {printf("%d\n",l.data[i]);}}//测试int main() {struct list alist = {};insert(&alist,100,alist.length+1);insert(&alist,200,alist.length+1);insert(&alist,300,alist.length+1);insert(&alist,400,alist.length+1);delete(&alist,1);printf("%d\n",alist.length);show(alist);printf("%d\n",get(&alist,4));printf("%d\n", locate(&alist,300));printf("%d\n", get(&alist,1));return 0;}插⼊算法分析:假设线性表中含有n个元素,在插⼊元素时,有n+1个位置可以插⼊,因为要保证数据是连续的每个位置插⼊数据的概率是: 1/(n+1)在i的位置插⼊时,要移动的元素个数为:n - i + 1算法时间复杂度为:O(n)删除算法分析:假设线性表中含有n个元素,在删除元素时,有n个位置可以删除每个位置插⼊数据的概率是: 1/n在i的位置删除时,要移动的元素个数为:n - i算法时间复杂度为:O(n)插⼊与删除的不⾜顺序表在进⾏插⼊和删除操作时,平均要移动⼤约⼀半的数据元素,当存储的数据量⾮常⼤的时候,这⼀点需要特别注意;简单的说,顺序表在插⼊和删除时的效率是不够好的;特别在数据量⼤的情况下;顺序表总结:1.顺序表是⼀维数组实现的线性表2.逻辑上相邻的元素,在存储结构中也是相邻的3.顺序表可实现随机读取优缺点:优点:⽆需为了表⽰元素直接的逻辑关系⽽增加额外的存储空间可⽅便的随机存取表中的任⼀节点缺点:插⼊和删除运算不⽅便,需要移动⼤量的节点顺序表要求占⽤连续的存储空间,必须预先分配内存,因此当表中长度变化较⼤时,难以确定合适的存储空间⼤⼩;顺序表节点存储地址计算:设第i个节点的存储地址为x设顺序表起始地址为loc,每个数据元素占L个存储单位计算公式为:x = loc + L * (i-1)如 loc = 100 i = 5 L = 4 则 x = 116线性表的链接存储实现线性表也可通过链接存储⽅式来实现,⽤链接存储⽅式实现的线性表也称为链表 Link List链式存储结构:1.可⽤任意⼀组存储单元来存储数据2.链表中节点的逻辑次序与物理次序不⼀定相同3.每个节点必须存储其后继节点的地址信息(指针)图⽰:单链表单链表指的是只能沿⼀个⽅向查找数据的链表,如上图每个节点由两个部分(也称为域)组成data域存放节点值得数据域next域存放节点的直接后继的地址的指针域(也称为链域)节点结构:每个节点只知道⾃⼰后⾯⼀个节点却不知道⾃⼰前⾯的节点所以称为单链表图⽰:带有head节点的单链表:单链表的第⼀个节点通常不存储数据,称为头指针,使⽤头指针来存储该节点的地址信息,之所以这么设计是为了⽅便运算;单链表特点:其实节点也称为⾸节点,没有前驱,所以头指针要指向该节点,以保证能够访问到起始节点;头指针可以唯⼀确定⼀个链表,单链表可以使⽤头指针的名称来命名;终端节点也称尾节点,没有后继节点,所以终端节点的next域为NULL;除头结点之外的⼏点称为表结点为⽅便运算,头结点中不存储数据单链表数据结构定义//数据结构定义typedef struct node {struct node *next;int data,length;} Node, *LinkList;/** typedef 是⽤来取别名的* Node 是struct node 的别名* *LinkList 是 struct node *的别名* 后续使⽤就不⽤在写struct关键字了*/运算:初始化⼀个空链表有⼀个头指针和⼀个头结点构成假设已定义指针变量L,使L指向⼀个头结点,并使头结点的next为NULL//时间复杂度 :O(1)LinkList initialLinkList() {// 定义链表的头结点LinkList head;//申请空间head = malloc(sizeof(struct node));//使头结点指向NULLhead->next = NULL;return head;}求表长从头指针开始遍历每个节点知道某个节点next为NULL为⽌,next不为空则个数len+1;//求表长时间复杂度 :O(n)int length(LinkList list){int len = 0;Node *c = list->next;while(c != NULL){len+=1;c = c->next;}return len;}读表元素给定⼀个位置n,获取该位置的节点遍历链表,过程中若某节点next为NULL或已遍历个数index=n则结束循环//从链表中获取第position个位置的节点时间复杂度 :O(n)Node *get(LinkList list, int position) {Node *current;int index = 1;current = list->next;//如果下⾯还有值并且还没有到达指定的位置就继续遍历要和查找元素区别开这就是⼀直往后遍历直到位置匹配就⾏了 while (current != NULL && index < position) {current = current->next;index += 1;}if (index == position) {return current;}return NULL;}定位对给定表元素的值,找出这个元素的位置遍历链表,若某节点数据域与要查找的元素data相等则返回当前遍历的次数index//求表head中第⼀个值等于x的结点的序号(从1开始),若不存在这种结点,返回结果为0 时间复杂度 :O(n)int locate(LinkList list,int data){int index = 1;Node *c;c = list->next;while (c != NULL){if (c->data == data){return index;}index+=1;c = c->next;}return 0;}插⼊在表的第i个数据元素结点之前插⼊⼀个以x为值的新结点new获取第i的节点的直接前驱节点pre(若存在),使new.next = pre.next;pre.next = new;//在表head的第i个数据元素结点之前插⼊⼀个以x为值的新结点时间复杂度 :O(n)void insert(LinkList list, int position, int data) {Node *pre, *new;if (position == 1) {//若插⼊位置为1 则表⽰要插⼊到表的最前⾯即head的后⾯pre = list;} else {//pre表⽰⽬标位置的前⼀个元素所以-1pre = get(list, position - 1);if (pre == NULL) {printf("error:插⼊的位置超出范围");exit(0);}}new = malloc(sizeof(Node));new->data = data;new->next = pre->next;pre->next = new;list->length += 1;}删除删除给定位置的节点获取⽬标节点target的直接前驱节点pre(若pre与⽬标都有效),pre.next = target.next; free(target);//删除链表中第position个位置的节点时间复杂度 :O(n)void delete(LinkList list,int position){//获取要删除节点的直接前驱Node *pre;if (position == 1){ //如要删除的节点是第⼀个那直接前驱就是头结点pre = list;}else{pre = get(list,position-1);}////如果⽬标和前驱都存在则执⾏删除if (pre != NULL && pre->next != NULL){Node *target = pre->next; //要删除的⽬标节点//直接前驱的next指向⽬标的直接后继的nextpre->next = target->next;free(target);printf("info: %d被删除\n",target->data);list->length -= 1;}else{printf("error:删除的位置不正确!");exit(1);}}创建具备指定数据节点的链表//效率⽐较差算法时间复杂度 :O(n^2)LinkList createLinkList1(){LinkList list = initialLinkList();int a;//输⼊的数据int index = 1; //记录当前位置scanf("%d",&a);while (a != -1){ // O(n)insert(list,index++,a); // O(n^2) 每次都要从头遍历链表scanf("%d",&a);}return list;}//尾插算法记录尾节点从⽽避免遍历时间复杂度 :O(n)LinkList createLinkList2(){LinkList list = initialLinkList();int a;//输⼊的数据Node *tail = list;//当前的尾部节点scanf("%d",&a);while (a != -1){ // O(n)Node * newNode = malloc(sizeof(Node)); //新节点newNode->next = NULL;newNode->data = a;tail->next = newNode;//尾部节点的next指向新节点tail = newNode;//新节点作为尾部节点scanf("%d",&a);}return list;}//头插算法每次插到head的后⾯,不⽤遍历但是顺序与插⼊时相反时间复杂度 :O(n)LinkList createLinkList3(){LinkList list = initialLinkList();int a;//输⼊的数据Node * head = list;scanf("%d",&a);while (a != -1){ // O(n)Node * newNode = malloc(sizeof(Node)); //新节点newNode->next = NULL;newNode->data = a;newNode->next = head->next;//将原本head的next 交给新节点;head->next = newNode;//在把新节点作为head的next;scanf("%d",&a);}return list;}优缺点优点:在⾮终端节点插⼊删除时⽆需移动其他元素⽆需预分配空间,⼤⼩没有限制(内存够的情况)缺点:⽆法随机存取读取数据慢链表与顺序表的对⽐:操作顺序表链表读表元O(1)O(n)定位O(n)O(n)插⼊O(n)O(n)删除O(n)O(n)。

数据结构分类

数据结构分类

数据结构分类数据结构是计算机科学中的一个重要概念,它用于存储和组织数据以便有效地访问和操作。

根据数据元素之间的关系和操作的性质,数据结构可以被分为不同的类型。

本文将介绍常见的数据结构分类,并讨论每种分类的特点和应用。

1. 线性结构线性结构是最简单且最常见的数据结构之一,其特点是所有的数据元素都排列成一条直线。

线性结构包括顺序表、链表、栈和队列等。

顺序表是一种用连续的存储单元依次存储数据元素的结构,可以通过下标直接访问元素。

链表则是通过指针将元素链接在一起,允许在任意位置插入和删除元素。

栈是一种特殊的线性结构,只允许在一端插入和删除元素,满足后进先出(LIFO)的原则。

队列也是一种特殊的线性结构,只允许在一端插入,在另一端删除,满足先进先出(FIFO)的原则。

2. 非线性结构非线性结构中的数据元素并不是一对一的关系,而是多对多的关系。

其中最常见的非线性结构是树和图。

树结构由一组节点和边组成,每个节点可以有多个子节点,但只有一个父节点,顶端的节点称为根节点。

树结构常用于表示层次关系,例如文件系统。

图结构是一种包含节点和边的集合,节点之间的连接关系可以是任意的,图结构可以用来表示各种复杂的关系网络,比如社交网络和网页链接。

3. 数据结构的扩展除了线性结构和非线性结构,还有一些特殊的数据结构用于解决特定的问题。

常见的扩展结构包括散列表、堆、树状数组和并查集等。

散列表采用哈希函数将元素映射到一个存储位置,以实现快速的插入、删除和查找操作。

堆是一种优先级队列的实现方式,可以高效地找到最大或最小元素。

树状数组可以用于快速求取前缀和等操作。

并查集用于维护不相交集合的数据结构,常用于解决连通性问题。

总结数据结构是计算机科学中非常重要的概念,不同的数据结构适用于解决不同类型的问题。

线性结构适用于有序的数据关系,非线性结构适用于多对多的关系。

此外,扩展的数据结构可以帮助我们更高效地解决一些特殊问题。

掌握不同数据结构的特点和应用,对于算法设计和程序优化至关重要。

数据结构之线性表

数据结构之线性表
什么是线性表
线性表是最简单、最基本、最常用的数据结构。线性表是线性结构的抽象(Abstract),线性 结构的特点是结构中的数据元素之间存在一对一的线性关系。这种一对一的关系指的是数据 元素之间的位置关系,即:( 1)除第一个位置的数据元素外,其它数据元素位置的前面都 只有一个数据元素;( 2)除最后一个位置的数据元素外,其它数据元素位置的后面都只有 一个元素。也就是说,数据元素是一个接一个的排列。因此,可以把线性表想象为一种数据 元素序列的数据结构。
单链表的存储
链表是用一组任意的存储单元来存储线性表中的数据元素(这组存储单元可以是连续的,也 可以是不连续的)。那么,怎么表示两个数据元素逻辑上的相邻关系呢?即如何表示数据元 素之间的线性关系呢?为此,在存储数据元素时,除了存储数据元素本身的信息外,还要存 储与它相邻的数据元素的存储地址信息。这两部分信息组成该数据元素的存储映像(Image), 称为结点(Node)。把存储据元素本身信息的域叫结点的数据域(Data Domain),把存储与它 相邻的数据元素的存储地址信息的域叫结点的引用域(Reference Domain)。因此,线性表 通过每个结点的引用域形成了一根“链条”,这就是“链表”名称的由来。 如果结点的引用域只存储该结点直接后继结点的存储地址,则该链表叫单链表(Singly Linked List)。把该引用域叫 next。单链表结点的结构如图所示,图中 data 表示结点的数 据域。
data = val; next = p; }
//构造器
public DbNode(DbNode<T> p) {
next = p; }
//构造器 public DbNode(T val) { data = val; next = null; }

数据结构线性表

数据结构线性表

数据结构---线性表线性表代码主要参考严蔚敏《数据结构(c语言版)》,有部分改动线性表的定义定义•线性表是具有相同的数据类型的n(n >= 0)个数据元素的有限序列,当n=0时线性表为一个空表•用L表示线性表则L = (a1,a2,a3,…,ano a1为表头元素,an为表尾元素o a1无直接前驱,an无直接后继特点•表中元素个数有限•表中元素具有逻辑上的顺序,表中元素有先后次序•表中元素都是数据元素•表中元素的数据类型都相同,每个元素占的空间大小一致要点数据项、数据元素、线性表的关系线性表由若干个数据元素组成,而数据元素又由若干个数据项组成,数据项是数据的不可分割的最小单位。

其中姓名,学号等就是数据项线性表的顺序表示顺序表的定义顺序表是指用一组地址连续的存储单元依次存储信息表中的数据元素,从而使得逻辑相邻的两个元素在物理位置上也相邻预先定义(为了代码可以运行)#define True 1#define False 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2typedef int Status;第n个元素的内存地址表示为LOC(A) + (n-1)*sizeof(ElemType)假定线性表的元素类型为ElemType,则线性表的顺序存储类型描述为typedef int ElemType ;#define MaxSize 50typedef struct{ElemType data[MaxSize];int length;}SqList;一维数组可以是静态分配的,也可以是动态分配的。

静态分配后大小和空间都固定了,下面使用动态分配的形式typedef int ElemType ;#define InitSize 100 //表长度的初始大小定义#define ListIncreasement 10 //线性表存储空间的分配增量typedef struct{ElemType *data;int MaxSize,length;}SeqList;顺序表的初始化顺序表的初始化,&是C++的引用,可以使用指针代替Status InitList(SeqList &L){L.data = (ElemType *) malloc(InitSize * sizeof(ElemType));if(! L.data) exit(OVERFLOW);//存储分配失败L.length = 0;L.MaxSize = InitSize;return OK;}顺序表的插入在顺序表L的第i(1<= i <= L.length +1)个位置插入新元素e,需要将第n 个至第i (共n-i+1)个元素向后移动一个位置【最后一个到倒数第n-i+i个元素向后移动一位】。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构之线性结构(一,表结构)作者:冲出宇宙时间:2006-10-24修改:2006-11-3转载请注明作者。

作者主要参考了上面的资料(因为wikipedia上不去)和部分较新学术论文(一般来自于acm, IEEE和springer),如果有什么疑问,您可以参考以上资料,我会努力的把重要的论文罗列在文章里面。

本文主要介绍了线性数据结构部分,线性数据的分类来自于wikipedia,见页面:/topic/list-of-data-structures。

1 线性数据结构的分类线性数据结构的分类如下:.表(List).数组(Array).位图(Bitmaps).图片(Images).数字高程模型(Heightfield).动态数组(Dynamic array).平行数组(Parallel array).向量(Vector).集合(Set).链表(Linked list).松散链表(Unrolled linked list).Xor链表(Xor linked list).可变表(VList).联合数组(Associative array).散列表(Hash table).跳跃表(Skip list).栈(Stack).队列(Queue).优先队列(Priority queue).双向队列(Deque).间隙缓冲(Gap buffer)2 表(List)表是一个抽象数据结构(ADT, abstract data type,它表示的是一种接口,而不是实现),它表示的是一个有序实体集合。

但是,表并没有一个确定的接口。

比如,可变的表会包含4种操作:1,建立空表;2,测试表十分为空;3,在前端插入一个数据到表里面去;4,返回一个新表,包含了除了第一个元素(也可能是最后一个元素)以外的其它所有元素。

在现实中,表通常由数组或者链表实现,因为它们都和表具有一些共同点。

通常人们会把表当成是链表的别名。

序列也是表的另外一个名字,它突出了实体之间的顺序关系。

2.1 数组(Array)和数组相关的有向量、表(一维数组实现)、矩阵(二维数组实现),数组是一种最简单的数据结构。

数组包含了一系列的数据元素,而且这些元素一般都是同样的大小和类型。

数组里面的元素通过索引来访问,一般的说,索引是一段连续的整数范围,但是,它也可以为任何有序的数值。

数组可以是多维的,比如,2维数组。

3维或者以上的数组很少被采用。

时间复杂度:通过索引访问数组极快(o(1)),但是,从数组里面插入或者删除一个数据的代价很高(o(n)),特别是删除数据可能会造成数组空闲太多,和插入数据造成数组空间不够。

这些可以利用动态数组来解决。

链表虽然插入或者删除数据较快,可是访问其中的元素十分慢(o(n))。

空间复杂度:数组是最不浪费内存的数据结构,比较起来散列表是十分浪费内存的。

数组不占用任何额外的空间(最多增加一个保存数组的大小,4字节)。

程序:大部分程序都内置数组类型。

2.1.1 位图(Bitmap)位图其实是一个数组,数组的每个元素都是布尔值(0/1)。

常常使用字节数组来表示位图,因为每个字节可以表示8个位图的元素。

位图最常用在空间处理上,比如,磁盘分配。

根据位图的个性,所有用来表示是和否的地方都可以使用它。

因为一个字节的位图可以表示8个是和否,所以,它也常常用来压缩数据。

不过,访问一位比访问一个字节会慢很多,访问一个字节比访问一个int会慢很多(如果32位机器)。

2.1.1.1 图片(Images)图片也叫数字图片。

图片是一个2维结构,每个元素对应于图片上的某点。

每个元素的大小根据其要显示的效果而变化,主要有1位,8位,16位,24位,32位几种。

根据显示色彩的不同,一般可以分为黑白、灰度、彩色(8位,16位,24位,32位)、抖动色这几种。

一般的图片都由很多像素组成,所以,一个图片占用的空间十分大。

一般情况下,压缩是必须的。

最常见的几种压缩格式为:gif(lzw压缩)、png(未知)、jpg(DCT压缩)、jpg2000(小波压缩)。

2.1.1.2 数字高程模型(Heightfield 也叫:Digital Elevation Model, or DEM)DEM也是一个位图,只是,它每个点表示的意思是高度。

上图是根据dem图还原出来的火星地图。

下图是一个加上了色彩的DEM 图。

2.1.2 动态数组(Dynamic Array)它同时的名字还有:可增数组(growable array), 可变长数组(resizable array), 动态表(dynamic table), 数组表(array list)。

它是一种能够自动调整自己大小的数组。

当加入一个新数据时,如果动态数组没有空间了,它会申请一个新的空间,然后把旧数据全部拷贝过去,释放旧空间(有些动态数组的实现并不会拷贝旧数据过去,也不会释放旧空间)。

一般的时候,新分配的空间大小都是原来空间大小的一个倍数,保证有一部分空间是空闲的。

简单的计算一下,就能发现加入一个数据的平均花销是o(1)。

同样的道理,删除一个数据的时候,如果空闲的空间太多了,动态数组也可能申请一个新空间,然后删除旧空间。

申请新空间时,申请多大的空间是一个值得考虑的问题。

目前来说,一般认为申请的新空间为旧空间的1.4-4倍之间都是合适的,最常见的是2倍。

在浪费空间上面,有人证明了至少需要浪费o(n^1/2)这么多空间才能保证插入和删除都在常数时间内完成。

Andrej Brodnik, Svante Carlsson, Erik D. Demaine, J. Ian Munro, and R obert Sedgewick. Resizable Arrays in Optimal Time and Space (1999). Workshop on Algorithms and Data Structures, pp.37–48动态数组还有很多变形,比如,为了加快删除的速度,可以把开始的数据放到中间(如图):这里,第一个数据放到数组的中间,第i个数据放到(i+n/2)%n的位置。

当删除数据的时候,最多只需要移动n/2次就行了。

当然了,需要保存一个开始的位置和实际的数组长度。

比如,删除掉3,得到:5 6 7 1 2 4 *,删除掉7得到:* 5 6 1 2 4 *。

2.1.3 平行数组(Parallel Array)平行数组最初是为了在不支持记录(对象)的环境下面使用的。

它把一个记录切分成多个基本数组,每个数组的长度一样。

比如,struct Info{int age;char*name;}Info person[2];我们可以使用平行数组表示为:int ages[] = {1,2};char *names[] = {"good","zzp"};平行数组拥有的结构简单,访问速度快速,同时,还能够节省结构可能需要使用的指针(某些语言需要指针,某些不需要。

比如,c语言不需要指针,而j ava语言需要指针)。

但是,它的最大缺点是当记录含有很多域的时候,平行数组将变得极难维护,同时,对它的顺序访问并不会实际问顺序的位置,对基于访问局部性的缓冲策略是一大妨碍。

2.1.4 向量(Vector)向量是一个十分基本的动态数组,它具有和动态数组一样的性质。

2.1.5 集合(Set)集合是包含了一系列的数据,这些数据没有经过排序而且也没有重复的数据。

它比较严格的对应于数学上的集合的概念。

集合必须是有限的,这点和数学上的集合不同。

集合一般来说必须支持以下几种操作:1) 判断一个元素是否在集合里面;2) 对集合的所有元素进行遍历;3) 2个集合之间的交和并操作。

虽然联合数组是通常的建立集合的数据结构,但是,它并不能很好的支持集合之间的交并操作。

2.1.6 链表(Linked list)链表是计算机科学里面最基础的数据结构之一。

它包含一系列的节点,每个节点含有任意多个的域和一个或者两个的指针。

指针可能指向前面一个节点也可能指向后面一个节点。

增加或者删除一个指定节点都是常数时间,但是随机访问一个节点的代价是o(n)。

常用的3种链表为:单链表、双向链表和循环链表。

见图。

单链表是最简单的链表形式,每个节点有一个指针,指向后一个节点。

最后的那个节点的指针指向null。

它访问任何节点都必须从头开始,一步一步的走到待访问的节点。

双向链表是一个复杂一点的结构,它的每个节点包含2个指针,一个指向前一个节点,一个指向后一个节点。

同样,第一个节点的前向指针指向null,最后那个节点的后向指针指向null。

它可以从头遍历也可以从尾遍历。

循环链表把第一个节点和最后一个节点链接了起来。

它可以在单向链表或者双向链表的基础上构建。

第一个节点的前向指针指向最后的那个节点,而最后那个节点的后向指针指向第一个节点。

哨兵节点(Sentinel Nodes)是一个额外的未使用的节点,它常常在链表的开头或者结尾。

它用来加快或者简化某些计算的过程。

2.1.6.1 松散链表(unrolled linked list)链表的最大问题是访问非聚集(即连续的访问并不是访问连续的内存空间),松散链表的主要目的是为了解决这个问题(从而显著的提高缓冲性能)。

松散链表改进了普通链表的结构,现在每个节点可以包含多个数据。

每个节点包含多个元素。

其基本结构为:struct Node{Node next;//下一个节点int maxElement;// 节点包含的最大元素个数Array Elements;// 本节点包含的元素个数}链表里面的每个节点对应一个元素,而松散链表每个节点可以包含最多ma xElements个元素。

从其定义可以看出,节点可以包含少于maxElement个元素。

那么,我们需要一个规则来决定如何包含元素到节点里面,同时尽量保证节点的elements数组空间利用率高。

基本的松散链表使用的是1-2规则,也就是说,如果节点里面包含的元素个数将大于可以包含的元素个数,那么,就把这个节点分裂成2个节点,而如果这个节点包含的元素数将小于maxElement/2,就从邻近的节点借一些元素过来,如果邻近的节点在借给它之后,拥有的节点数小于m axElement/2的话,就把这2个节点合并成一个节点。

总之,1-2规则就是保证(只有一个节点例外)每个节点至少空间利用在1/2。

按照这个规律,最常使用的还有2-3规则和3-4规则。

再大一些的规则就十分难以控制了,因为笔者曾经写过3-4规则的B*树,代码的复杂程度让我不敢重新再看一遍了。

至于空间性能方面,每个节点至少有1/2的空间利用率,在平衡的情况下,每个节点的利用率应该是(1+1/2)/2=3/4。

相关文档
最新文档