【2010年顺序表】
42. (13分)设将n (n>1)个整数存放到一维数组R中。试设计一个在时间和空间两方面都尽可能高效的算法。将R中保存的序列循环左移p (0
【2012年链表】
42. 假定采用带头结点的单链表保存单词,当两个单词有相同的后级时,则可共享相同的后缀存储空间,例如,“loading”和“being”的存储映像如下图所示。

设str1和 str2分别指向两个单词所在单链表的头结点,链表结点结构为data | next,请设计一个时间上尽可能高效的算法,找出由str1和str2所指向两个链表共同后缀的起始位置(如图中字符i所在结点的位置p)。要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度。
本节分为四小节讲解。
由n (n≥0)个相同类型的元素组成的有序集合。
L=(a1,a2,…,ai−1,ai,ai+1,…,an)L=(a_1,a_2,…, a_{i-1},a_i,a_{i+1},…, a_n) L=(a1,a2,…,ai−1,ai,ai+1,…,an)
注意:
本小节描述的是线性表的逻辑结构,是独立于存储结构的!
简称:顺序表

顺序表的定义:
#define Maxsize 50 //定义线性表的长度
typedef struct
{ElemType data[Maxsize] ; //顺序表的元素int len; //顺序表的当前长度
}SqList; //顺序表的类型定义
优点:
缺点:

代码片段:
//判断插入位置i是否合法(满足1≤i≤len+1 )
//判断存储空间是否已满(即插入x后是否会超出数组长度)
for(int j = L.len; j >= i; j--) //将最后一个元素到第i个元素依次后移一位L.data[j] = L.data[j-1] ;
L.data[i-l] = x; //空出的位置i处放入x
L.len++; //线性表长度加1

代码片段:
//判断删除位置i是否合法(满足1≤i≤len)
e = L.data[i-1] ; //将被删除的元素赋值给e
for(int j = i; j < L.len; j++) //将删除位置后的元素依次前移L.data[j-1] = L.data[j];
L.len--; //线性表长度减1
#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];
#include #define MaxSize 50
typedef int ElemType; //顺序表存储其他类型元素时,可以快速完成代码修改
//静态分配
typedef struct
{ElemType data[MaxSize];int length; //当前顺序表中有多少个元素
}SqList;动态分配
//#define InitSize 100
//typedef struct
//{
// ElemType *data;
// int capacity; //动态数组的最大容量
// int length;
//}SeqList;//顺序表的插入,因为L会改变,因此这里要用引用,i是插入的位置
bool ListInsert(SqList &L, int i, ElemType element)
{//判断i是否合法if (i < 1 || i > L.length + 1){return false;//未插入成功返回false}//如果存储空间满了,不能插入if (L.length == MaxSize){return false;//未插入成功返回false}//把后面的元素依次往后移动,空出位置,来放要插入的元素for (int j = L.length; j >= i; j--){L.data[j] = L.data[j - 1];}L.data[i - 1] = element; //放入要插入的元素L.length++;//顺序表长度要加1return true;//插入成功返回true
}//打印顺序表
void PrintList(SqList L) //不需要改变顺序表L的内容,不需要引用
{int i;for (i = 0; i < L.length; i++){printf("%3d", L.data[i]);//为了打印到同一行,不用换行}printf("\n");
}//顺序表的初始化及插入操作实战
int main()
{SqList L; //定义一个顺序表,变量Lbool ret; //ret用来查看函数的返回值,布尔型是True,或者FalseElemType del; //要删除的元素//首先手动在顺序表中赋值 - 放置元素L.data[0] = 1;L.data[1] = 2; L.data[2] = 3;L.length = 3;//设置长度ret=ListInsert(L,2,60);//大驼峰命名法if (ret) //等价于if (true == ret){printf("insert SqList success\n");PrintList(L);}else{printf("insert SqList failed\n");}return 0;}
运行结果:

成功在第二个位置插入了60。
#include #define MaxSize 50
typedef int ElemType; typedef struct
{ElemType data[MaxSize];int length;
}SqList;bool ListInsert(SqList &L, int i, ElemType element)
{if (i < 1 || i > L.length + 1){return false;}if (L.length == MaxSize){return false;}for (int j = L.length; j >= i; j--){L.data[j] = L.data[j - 1];}L.data[i - 1] = element;L.length++;return true;
}void PrintList(SqList L)
{int i;for (i = 0; i < L.length; i++){printf("%3d", L.data[i]);}printf("\n");
}//删除数据表中的元素,因为L会改变,因此这里要用引用,i是删除的位置,e是为了获取被删除的元素的值
bool ListDelete(SqList &L,int i, ElemType &e)
{//判断删除的元素的位置是否合法if (i < 1 || i > L.length){return false;//一旦走到return函数就结束了}e = L.data[i - 1];//首先保存要删除元素的值int j;for (j = i; j < L.length; j++){L.data[j - 1] = L.data[j];}L.length--;//顺序表长度减1return true;
}//查找某个元素的位置,找到了会返回对应位置,没找到就返回0
int LocateElem(SqList L, ElemType element)
{int i;for (i = 0; i < L.length; i++){if (element == L.data[i]){return i + 1;//因为i是数组的下标,加1以后才是顺序表的下标}}return 0;//循环结束没找到
}//顺序表的删除和查找操作实战
int main()
{SqList L; //定义一个顺序表,变量Lbool ret; //ret用来查看函数的返回值,布尔型是True,或者False//首先手动在顺序表中赋值 - 放置元素L.data[0] = 1;L.data[1] = 2;L.data[2] = 3;L.length = 3;//设置长度ret = ListInsert(L, 2, 60);if (ret) {printf("insert SqList success\n");PrintList(L);}else{printf("insert SqList failed\n");}printf("---------------------------------------------\n");ElemType del; 删除的元素存入del内ret = ListDelete(L, 1, del);if (ret) //等价于if (true == ret){printf("delete SqList success\n");printf("del element = %d\n", del);PrintList(L);//顺序表打印}else{printf("delete SqList failed\n");}int pos;//存储元素位置pos = LocateElem(L, 60);if (pos){printf("find this element\n");printf("element pos = %d\n", pos);}else{printf("don't find this element\n");}return 0;
}
运行结果:


顺序表有一些缺点:
- 插入和删除操作移动大量元素。
- 数组的大小不好确定。
- 占用一大段连续的存储空间,造成很多碎片。

typedef struct LNode //单链表结点类型
{ElemType data; //数据域struct LNode *next; //指针域
//当结构体中用到结构体本身时,名字无法省略
}LNode, *LinkList;
链表结点:


头指针:链表中第一个结点的存储位置,用来标识单链表。
头结点:在单链表第一个结点之前附加的一个结点,为了操作上的方便。
若链表有头结点,则头指针永远指向头结点,不论链表是否为空,头指针均不为空,头指针是链表的必须元素,其标识一个链表。
头结点是为了操作的方便而设立的,其数据域一般为空,或者存放链表的长度。有头结点后,对在第一结点前插入和删除第一结点的操作就统一了,不需要频繁重置头指针。但头结点不是必须的。
优点:
缺点:
q = (LNode*)malloc(sizeof(LNode))q -> data = x;
q -> next = p -> next;
p -> next = q;
p -> next=q;
q -> next = NULL;


p = GetElem(L,i-1);//查找删除位置的前驱节点
q = p -> next;
p -> next = q -> next;//结点q断链
free(q);//必须


LNode *p = L -> next;
int j=1;
while (p && j < i)
{p = p -> next;j++;
}
return p;
LNode *p = L -> next;
while (p != NULL && p -> data != e)
{p = p -> next;
}
return p;
顺序表优点:
- 可以随机存取(根据表头元素地址和元素序号)表中任意一个元素。
- 存储密度高,每个结点只存储数据元素。
顺序表缺点:
- 插入和删除操作需要移动大量元素。
- 线性表变化较大时,难以确定存储空间的容量。
- 存储分配需要一整段连续的存储空间,不够灵活。
链表中逻辑上相邻的两个元素在物理位置上不相邻。
头指针:链表中第一个结点的存储位置,用来标识单链表。
头结点:在单链表第一个结点之前附加的一个结点,为了操作上的方便。
若链表有头结点,则头指针永远指向头结点,不论链表是否为空,头指针均不为空,头指针是链表的必须元素,其标识一个链表。
头结点是为了操作的方便而设立的,其数据域一般为空,或者存放链表的长度。有头结点后,对在第一结点前插入和删除第一结点的操作就统一了,不需要频繁重置头指针。但头结点不是必须的。
链表的优点:
- 插入和删除操作不需要移动元素,只需要修改指针。
- 不需要大量的连续存储空间。
链表的缺点:
- 单链表附加指针域,也存在浪费存储空间的缺点。
- 查找操作时需要从表头开始遍历,依次查找,不能随机存取。
上一篇:抑制快艇中的波浪意味着什么
下一篇:我的大脑崩溃了