线性表
是一种逻辑结构, 不特指某一具体在内存中以某种物理结构存储的线性结构
线性表是指具有相同特性数据元素的有限序列
分类
[顺序表]
[链表]
顺序表
物理结构(在内存中存储方式):
连续存储, 且静态分配
数据域
- 存放数据的data数组
- 记录数据长度的length
结构定义:
/* *ElemType是宏定义或者typedef指定的类型名 *这里typedef是将这个结构体类型命名为SqList */ #define MAX_SIZE 100 typedef int ElemType; /**同 #define ElemType int */ typedef struct{ ElemType data[MAX_SIZE]; //data数组 int length; //顺序表长度 }SqList;//Sequence List /**可以使用typedef或#define修改SqList的存储数据类型, 代码重用性强**/
关于
#define
和typedef
#define
只是简单的文本替换 , 除了简写基本类型名(或者已经定义类型名)外还可以用来定义常量typedef
是对一个类型名起一个别名(原类型名依然可用), 可以对结构体这种复杂类型重命名顺序表的存储范围[0, MAX_SIZE - 1] (参照数组的范围), MAX_SIZE即为最大的长度
常用操作
插入: 在指定位置插入指定的数据元素
指定位置要满足当前
length
确定的范围eg : 若当前顺序表长度为5, 则顺序表中有效数据的存储下标范围为[0, 4](本例从0开始)
则插入元素的位置范围为[0, 5](数组中仅有5个有效元素, 插入位置不能>=7(下标为6))
/* *插入操作 *@param: l : 顺序表 *@param: pos : 指定插入位置 *@param: x : 指定插入数据 *@return : int(1 : 插入成功, 0 : 插入失败) */ int Insert(SqList &l, int pos, ElemType x){ if(l.length == MAX_SIZE) return 0;//数组已满,插入失败 if(pos < 0 || pos > l.length) return 0;//指定插入位置超出范围 for(int i = l.length - 1; i >= pos; --i) l.data[i + 1] = l.data[i]; l.data[pos] = x; ++(l.length); return 1; }
删除: 删除指定位置的元素(可返回该元素值)
指定位置要满足条件(不能删除不存在位置的元素)
范围为
[0, length - 1]
/* *删除操作 *@param: l : 顺序表 *@param: pos : 指定删除位置 *@param: x : 获取删除的元素值(删除成功时) *@return : int(1 : 删除成功, 0 : 删除失败) */ int Delete(SqList &l, int pos, ElemType &x){ if(pos < 0 || pos >= l.length) return 0;//指定删除位置超出范围 x = l.data[pos]; for(int i = pos; i < l.length - 1; ++i) l.data[i] = l.data[i + 1]; --(l.length); return 1; }
查询: 获取指定位置的元素
由于顺序表中存放元素的事数组, 可以依据存储位置(数组下标)随机访问
查询时间复杂度
O(1)
ps : 自然指定位置要满足length的范围
/* *查询操作 *@param: l : 顺序表 *@param: pos : 指定查询位置 *@param: x : 获取查询的元素值(查询成功时) *@return : int(1 : 查询成功, 0 : 查询失败) */ int GetElem(SqList l, int pos, ElemType &x){ if(pos < 0 || pos >= l.length) return 0;//超范围 x = l.data[pos]; return 1; }
打印: 遍历输出顺序表的元素
void Print(SqList l){ int len = l.length; if(len == 0) return;//线性表为空 for(int i = 0; i < len; ++i) printf("%d%c", l.data[i], i == len - 1 ? '\n' : ' '); }
获取长度
int ListLength(SqList l){ return l.length; }
初始化:顺序表数组长度赋0值
此时顺序表数组中或许有数据存在, 但不是
有效数据
,有效数据在[0, length)范围内void InitList(SqList &l){ l.length = 0; }
按值查询:查询指定元素在顺序表数组中的位置,若不含该元素返回-1
int LocateElem(SqList l, ElemType x){ for(int i = 0; i < l.length; ++i) if(l.data[i] == x) return ; return -1; }
特点:
- 存储密度大(无指针域)
- 支持随机访问(下标O(1)查找)和顺序访问
- 插入和删除操作时间复杂度为
O(n)
,要移动元素 - 静态分配空间, 存储空间固定
顺序表有序化:
线性表本身是无序的(有序是指元素之间按照某种既定规则排列), 可以通过一定的插入规则使其有序
eg : 顺序表递增插入
int GetInsertPos(SqList l, ElemType x){ for(int i = 0; i < l.length; ++i) if(l.data[i] > x) return i; return l.length; } int IncreseInsert(SqList &l, ElemType x){ int pos = GetInsertPos(l, x); return Insert(l, pos, x); }
代码整合(CodeUp1323)
#include <stdio.h> /** *CodeUp 1323 *Sequence List **/ #define MAX_SIZE 210 #define ElemType int #define Status int #define OK 1 #define ERROR 0 //宏定义 typedef struct{ ElemType data[MAX_SIZE]; int length; }SqList; int LocateElem(SqList l, ElemType x){ for(int i = 0; i < l.length; ++i) if(l.data[i] == x) return i; return -1; } int ListLength(SqList l){ return l.length; } void InitList(SqList &l){ l.length = 0; } Status GetElem(SqList l, int pos, ElemType &e){ if(pos < 0 || pos >= l.length) return ERROR; e = l.data[pos]; return OK; } Status Insert(SqList &l, int pos, ElemType x){ int len = ListLength(l); if(pos < 0 || pos > len || len == MAX_SIZE) return ERROR; for(int i = len - 1; i >= pos; --i) l.data[i + 1] = l.data[i]; l.data[pos] = x; ++(l.length); return OK; } Status Delete(SqList &l, int pos, ElemType &e){ if(pos < 0 || pos >= l.length) return ERROR; e = l.data[pos]; for(int i = pos; i < l.length - 1; ++i) l.data[i] = l.data[i + 1]; --(l.length); return OK; } void Print(SqList l){ int len = ListLength(l); for(int i = 0; i < len; ++i) printf("%d%c", l.data[i], i == len - 1 ? '\n' : ' '); } void Union(SqList &la, SqList lb){ int len_la = ListLength(la); int len_lb = ListLength(lb); for(int i = 0; i < len_lb; ++i){ if(LocateElem(la, lb.data[i]) == -1) Insert(la, len_la++, lb.data[i]); Print(la); } } SqList la, lb; int main(){ int m, n; int v; while(scanf("%d", &m) != EOF){ InitList(la); InitList(lb); for(int i = 0; i < m; ++i){ scanf("%d", &v); Insert(la, i, v); } scanf("%d", &n); for(int i = 0; i < n; ++i){ scanf("%d", &v); Insert(lb, i, v); } Print(la); Print(lb); Union(la, lb); printf("\n"); } return 0; }
链表
物理结构
- 链式存储,非连续
- 每个元素以结点形式存在
- 结点包含指针域和数据域
- 数据域: 存储数据
- 指针域: 前驱(后继)结点的地址
- 包含一个头结点,作为链表的首部
形式:
- 单链表
- 双向链表
- 循环链表
- 循环双链表
- 静态链表
单链表
结构定义
typedef struct Node{ ElemType data; Node * next; }LNode; //头结点不存储信息
新建结点
LNode * NewNode(ElemType x){ LNode * res = (LNode *)malloc(sizeof(LNode)); res->data = x; res->next = NULL; return res; }
插入操作:
- 头插法
void InsertHead(LNode * head, LNode *tmp){ tmp->next = head->next; head->next = tmp; }
- 尾插法
void InsertTail(LNode * head, LNode * tmp){ LNode * cur = head; while(cur->next) cur = cur->next; cur->next = tmp; }
两递增有序单链表的归并
void Merge1(LNode * A, LNode * B, LNode * &C){ C = A; LNode * p = A->next; LNode * q = B->next; free(B); LNode * r = C; while(p != NULL && q != NULL){ if(p->data <= q->data){ r->next = p; r = r->next; p = p->next; }else{ r->next = q; r = r->next; q = q->next; } } if(p != NULL) r->next = p; if(q != NULL) r->next = q; }
两递增有序单链表归并成递减
void Merge2(LNode * A, LNode * B, LNode * &C){ C = A; LNode * p = A->next; LNode * q = B->next; free(B); LNode * r; while(p != NULL && q != NULL){ if(p->data <= q->data){ r = p; p = p->next; }else{ r = q; q = q->next; } InsertHead(C, r); } while(p != NULL) { r = p; p = p->next; InsertHead(C, r); } while(q != NULL) { r = q; q = q->next; InsertHead(C, r); } }
整合(CodeUp1324):
#include <stdio.h> #include <stdlib.h> #define ElemType int typedef struct Node{ ElemType data; Node * next; }LNode; LNode * NewNode(ElemType x){ LNode * res = (LNode *)malloc(sizeof(LNode)); res->data = x; res->next = NULL; return res; } void InsertHead(LNode * head, LNode *tmp){ tmp->next = head->next; head->next = tmp; } void InsertTail(LNode * head, LNode * tmp){ LNode * cur = head; while(cur->next) cur = cur->next; cur->next = tmp; } void Merge1(LNode * A, LNode * B, LNode * &C){ C = A; LNode * p = A->next; LNode * q = B->next; free(B); LNode * r = C; while(p != NULL && q != NULL){ if(p->data <= q->data){ r->next = p; r = r->next; p = p->next; }else{ r->next = q; r = r->next; q = q->next; } } if(p != NULL) r->next = p; if(q != NULL) r->next = q; } void Merge2(LNode * A, LNode * B, LNode * &C){ C = A; LNode * p = A->next; LNode * q = B->next; free(B); LNode * r; while(p != NULL && q != NULL){ if(p->data <= q->data){ r = p; p = p->next; }else{ r = q; q = q->next; } InsertHead(C, r); } while(p != NULL) { r = p; p = p->next; InsertHead(C, r); } while(q != NULL) { r = q; q = q->next; InsertHead(C, r); } } void Print(LNode * head){ LNode * cur = head->next; printf("%d", cur->data); cur = cur->next; while(cur != NULL){ printf(" %d", cur->data); cur = cur->next; } printf("\n"); } int main(){ int m, n; int v; while(scanf("%d", &m) != EOF){ LNode * A = NewNode(-1); LNode * B = NewNode(-1); LNode * C; for(int i = 0; i < m; ++i){ scanf("%d", &v); InsertTail(A, NewNode(v)); } scanf("%d", &n); for(int i = 0; i < n; ++i){ scanf("%d", &v); InsertTail(B, NewNode(v)); } Merge1(A, B, C); Print(C); } return 0; }
特点
- 插入删除不需要移动元素, 修改指针域,free即可
- 动态分配
- 只能顺序访问, 不能随机访问
- 存储密度<1(指针域)