数据结构(2) 线性表
线性表的定义
线性表是具有相同数据类型的$n$个数据元素的有限序列
除第一个元素,每个元素有且仅有一个直接前驱,除最后一个元素,每个元素有且仅有一个后继
顺序表
线性表的顺序存储称为顺序表,用一组地址连续的存储单元依次存储线性表中的数据元素,因此逻辑上相邻的两个元素在物理位置上也相邻
顺序表描述1
2
3
4
5
6
7
8
9
10
11
12
13
14#define MaxSize 50
typedef struct {
ElemType data[MaxSize];//顺序表元素 静态分配
int length;//当前长度
}SqList;
#define InitSize 100
typedef struct {
ElemType *data;//顺序表元素 动态分配数组的指针
int MaxSize,length;//数组最大容量和当前元素个数
}SqList;
L.data = (ElemType *)malloc(sizeof(ElemType) * InitSize);//c
L.data = new ElemType(InitSize);//C++
顺序表最主要的特点是随机访问,通过首地址和元素序号可在O(1)的时间复杂度内找到指定的元素
插入操作
在顺序表L的第i个位置插入新元素e
1 | |
删除操作
删除顺序表L中第i个位置的元素,删除的元素用引用变量e返回1
2
3
4
5
6
7
8
9bool ListDelete(SqList &L, int i, ElemType &e) {
if (i < 1 || i > L.length)
return false;
e = L.data[i - 1];
for (int index = i; index < L.length; index++)
L.data[index-1] = L.data[index];
L.length--;
return true;
}
按值查找
在顺序表L中查找一个元素值等于e的元素,并返回其位置(非索引)
1 | |
1.长度为$n$的顺序表$L$,编写一个时间复杂度为、空间复杂度为的算法,该算法删除线性表中所以值为x的数据元素。
1 | |
2.从有序顺序表中删除所有其值重复的元素,使表中所有的值均不相同。
1 | |
3.将两个有序顺序表合并成一个新的有序顺序表,并由函数返回其结果顺序表。
1 | |
4.线性表中的元素递增有序存储于计算机内,设计算法完成在最短的时间内在表中查找数值为x的元素,若存在则将其与后继元素交换,否则插入表中并使表中元素仍然递增有序。
1 | |
5.设将个整数存放到一维数组R中。设计一个高效的算法将R中保存的序列循环左移个位置,即将R中的数据由变换为
算法思想:
令,
1 | |
6.长度为L的升序序列S,处在第[L/2]个位置的输称为S的中位数,两个序列的中位数是含它们所有元素升序序列的中位数,现有两个等长的升序序列A和B,设计算法找出序列A和序列B的中位数。
算法思想:
设为A的中位数,为B的中位数
- 若,则或即为所求中位数;
- 若,舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,两次舍弃的长度相等;
- 若,舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,两次舍弃的长度相等;
- 在保留的两个升序序列中,重复1,2,3,直到两个序列只含有一个元素为止,较小者为所求中位数;
1 | |
7.已知一个整数序列,其中,若存在,且,则称为A的主元素。例如,5为A的主元素,,则B中没有主元素。设计算法找出A的主元素并输出,否则输出-1;
1 | |
链表
由于顺序表的插入、删除操作需要移动大量元素,影响运行效率,由此引入线性表的链式存储,链式存储不需要使用地址连续的存储单元,即逻辑上相邻的元素不要求在物理位置上也相邻
单链表
通过一组任意的存储单元来存储线性表中的数据元素,每个链表节点除了存放元素本身,还需要存放指向后继的指针,其是非随机存取结构1
2
3
4typedef struct Node{
ElemType data; //XX
struct Node *next;//指针域
}Node,*LinkList;
通常用头指针来标识一个单链表,在单链表第一个节点前附加一个节点称为头节点,头节点的数据域不设任何信息
头指针和头节点:
1.不管带不带头节点,头指针始终指向链表的第一个节点
2.引入头节点,统一操作,统一空表和非空表的处理
采用头插法建立单链表
1 | |
采用尾插法建立单链表
1 | |
按序号查找节点值1
2
3
4
5
6
7
8
9
10
11
12
13Node* getElem(LinkList L,int i){
int j=1;
Node *p= L->next;//头节点
if(i == 0)
return L;
if(i < 1)
return null;
while(p && j < i){ //从第一个节点开始查找
p = p->next;
j++
}
return p;
}
按值查找表节点1
2
3
4
5
6
7Node* getElem(LinkList L,ElemType e){
Node *p= L->next;//头节点
while(p && p->data != e){ //从第一个节点开始查找
p = p->next;
}
return p;
}
插入节点操作1
2
3p = getElem(L,i-1);//查找插入位置i的前驱节点
s->next = p->next;
p->next = s;
删除节点操作1
2
3
4p = getElem(L,i-1);//查找删除位置i的前驱节点
q = p-next;
p->next = q->next;
free(q);
双链表
双链表通过prior和next,分别指向前驱节点和后继节点
双链表的按值查找和按位查找操作和单链表相同1
2
3
4
5typedef struct Node{
ElemType data;
struct Node *prior;
struct Node *next;
}Node,*DoubleList;
插入节点操作
1 | |
删除节点操作1
2
3
4
5p = getElem(L,i-1);//查找删除位置i的前驱节点
q = p-next;
p->next = q->next;
p->next->prior = p;
free(q);
静态链表
静态链表是由数组来描述线性表的链式存储结构,节点也有数据域data和指针域next1
2
3
4
5#define MaxSize 50
typedef struct{
ElemType data;
int next ;//下一个元素的数组下标
}StaticLinkList[MaxSize];
在带头结点的单链表L中,删除所有值为x的结点,并释放其空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void deleteX(LinkList &L){
Node *p = L->next,*pre = L;*q;//pre为p的前驱结点
while(p != null){
if(p->data == x){
q = p;
p = p->next;
pre->next = p;
free(q);
}
else{
pre = p;
p = p->next;
}
}
}设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
1
2
3
4
5
6
7
8//递归方法
void reversePrint(LinkList &L){
if(L->next != nulll){
reversePrint(L-next);
}
printf(L->data)
}
//借助栈编写在带头结点的单链表L中删除最小值结点的高效算法,假设最小值唯一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15LinkList deleteMinNode(LinkList &L){
Node *p = L->next,*pre = L;// pre为p的前驱结点
Node *minPre = pre,*min = p;//最小值的前驱和当前最小值
while(p!= null){
if(p->data < min->data){
min = p;
minPre = pre;
}
pre = p;
p = p->next;
}
minPre->next = min->next;
free(min);
return L;
}试编写算法将带头结点的链表逆置,且空间复杂度为$O(1)$
1
2
3
4
5
6
7
8
9
10
11LinkList reverse(LinkList L){
Node *p,*r;//p为工作指针,r为后继指针
p = L->next;
L->next = NULL;
while(p != NULL){
r = p->next;
p->next = L->next;
L->next = p;
p = r;
}
}