严蔚敏《数据结构》习题(一)
1.14 判断下列函数f(n)和g(n),当n → ∞ \rightarrow\infty →∞时,哪个函数增长最快?
(1) f(n)=102+ln(n!+ 1 0 n 3 10^{n^3} 10n3) g(n)=2n4+n+7
(2) f(n)=(ln(n!)+5)2 g(n)=13n2.5
(3) f(n)= n 2.1 + n 4 + 1 n^{2.1}+\sqrt{n^4+1} n2.1+n4+1 g(n)=(ln(n!))2+n
(4) f(n)= 2 ( n 3 ) + ( 2 n ) 2 2^{(n^3)}+(2^n)^2 2(n3)+(2n)2 g(n)= 2 ( n 2 ) + n 5 2^{(n^2)}+n^5 2(n2)+n5
答:
(1) lim n → ∞ 1 0 2 + l n ( n ! + 1 0 n 3 ) 2 n 4 + n + 7 = 0 \lim\limits_{n \to \infty} \frac{10^2+ln(n!+10^{n^3})}{2n^4+n+7}=0 n→∞lim2n4+n+7102+ln(n!+10n3)=0 *g(n)*增长快
In[1]:= Limit[(10^2 + Log[n! + 10^(n^3)])/(2 n^4 + n + 7), {n -> Infinity}]
Out[1]= 0
(2) lim n → ∞ ( l n ( n ! ) + 5 ) 2 13 n 2.5 = 0 \lim\limits_{n \to \infty} \frac{(ln(n!)+5)^2}{13n^{2.5}}=0 n→∞lim13n2.5(ln(n!)+5)2=0 *g(n)*增长快
In[2]:= Limit[(Log[n!] + 5)^2/(13 n^(2.5)), {n -> Infinity}]
Out[2]= 0
(3) lim n → ∞ n 2.1 + n 4 + 1 ( l n ( n ! ) ) 2 + n = ∞ \lim\limits_{n \to \infty} \frac{n^{2.1}+\sqrt{n^4+1}}{(ln(n!))^2+n}=\infin n→∞lim(ln(n!))2+nn2.1+n4+1=∞ *f(n)*增长快
In[3]:= Limit[(n^2.1 + Sqrt[n^4 + 1])/((Log[n!])^2 + n), {n -> Infinity}]
Out[3]= \[Infinity]
(4) lim n → ∞ 2 ( n 3 ) + ( 2 n ) 2 2 ( n 2 ) + n 5 = ∞ \lim\limits_{n \to \infty} \frac{2^{(n^3)+(2^n)^2}}{2^{(n^2)}+n^5}=\infin n→∞lim2(n2)+n52(n3)+(2n)2=∞ *f(n)*增长快
In[4]:= Limit[(2^(n^3) + (2^n)^2)/(2^(n^2) + n^5), {n -> Infinity}]
Out[4]= \[Infinity]
2.11 设顺序表va中的数据元素递增有序。试写一算法,将x插入到顺序表的适当位置上,以保持该表的有序性。
Status ListInsert(SqList &va ,ElemType x){
//顺序表va非递减有序,插入x并保序,0<=va.length<va.listsize
if(va.length >= va.listsize){ //存储空间已满
newbase = (ElemType*)realloc(va.elem,
(va.listsize + LISTINCREMENT)*sizeof(ElemType));
if(!newbase) exit(OVERFLOW); //空间分配失败
va.elem = newbase;
va.listsize += LISTINCREMENT;
}
i = va.length + 1; //从后往前
while(i > 1 && x < va.elem[i-1]){
va.elem[i] = va.elem[i-1]; //边比较边移动
i--;
}
va.elem[i] = x;
++va.length;
return OK;
}//ListInsert
完整C实现:(假设所有元素为整型)
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define LIST_INIT_SIZE 100 //初始分配容量
#define LISTINCREMENT 10 //分配增量
typedef int Status;
typedef struct {
int *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配容量
}SqList;
Status InitList(SqList &va){//建表
va.elem = (int*)malloc(LIST_INIT_SIZE*sizeof(int));
if(!va.elem){
printf("建表出错\n");
exit(OVERFLOW);
}
va.length = 0;
va.listsize = LIST_INIT_SIZE;
return OK;
}
Status InputList(SqList &va,int n){//输入
int i;
if(n < 1 || n > va.listsize){
printf("输入出错\n");
return ERROR;
}
printf("请按照非递减原则输入元素,以回车隔开:\n");
for(i = 1;i <= n;i++)
scanf("%d",&va.elem[i]);
va.length = n;
return OK;
}
Status OutputList(SqList va,int n,int flag){//输出
int i;
if(n < 1 || n > va.listsize) return ERROR;
if(va.length == 0) return ERROR;
printf((flag++) == 1 ? "\n原表为:\n" : "\n插入后:\n");
for(i = 1;i <= n;i++)
printf("%d ",va.elem[i]);
return OK;
}
Status ListInsert(SqList &va ,int x){//插入
int *newbase,i;
if(va.length >= va.listsize){ //存储空间已满
newbase = (int*)realloc(va.elem,
(va.listsize + LISTINCREMENT)*sizeof(int));
if(!newbase) exit(OVERFLOW); //空间分配失败
va.elem = newbase;
va.listsize += LISTINCREMENT;
}
i = va.length + 1;
while(i > 1 && x < va.elem[i-1]){
va.elem[i] = va.elem[i-1]; //边比较边移动
i--;
}
va.elem[i] = x;
++va.length;
return OK;
}
int main(){
SqList va;
int n,x;
InitList(va);
printf("请输入元素个数:");
scanf("%d",&n);
InputList(va,n);
OutputList(va,n,1);
printf("\n请输入带插入元素:");
scanf("%d",&x);
ListInsert(va,x);
OutputList(va,n+1,0);
return 0;
}
2.19 已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试写一高效的算法,删除表中所有值大于mink且小于maxk的元素(若表中存在这样的元素),同时释放被删结点空间,并分析你的算法的时间复杂度(注意: mink和maxk是给定的两个参变量,它们的值可以和表中的元素相同,也可以不同)。
Status ListDelete(LinkList &L,int mink,int maxk){
//带头结点L,非递减有序,删除满足mink<x<maxk的元素并释放空间
q = L; p = L->next; //辅助指针
if(!L || !p) return ERROR;
if(mink >= maxk) return ERROR;
if(p->data >= maxk) return OK; //链表最小值大于maxk
while(p){
if(p->data > mink && p->data < maxk){
t = p;
q->next = p->next;
p = p->next;
free(t);
}
else{
q = p;
p = p->next;
}
}
return OK;
}//ListDelete
完整C实现:(假设所有元素为整型)
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct LNode {
int data;
struct LNode *next;
}LNode,*LinkList;
Status CreateList(LinkList &L){//建表,带头结点,尾插法
LinkList p,rear;
int i,n;
L = (LinkList)malloc(sizeof(LNode)); //头结点
if(!L) return ERROR;
L->data = NULL; L->next = NULL;
rear = L;
printf("请输入元素的个数:");
scanf("%d",&n);
printf("请按照非递减原则,输入元素:\n");
for(i = 0;i < n;i++){
p = (LinkList)malloc(sizeof(LNode));
if(!p) return ERROR;
p->data = NULL; p->next = NULL;
scanf("%d",&p->data);
rear->next = p;
rear = p;
}
rear->next = NULL;
return OK;
}
Status PrintList(LinkList L,int flag){//打印
LinkList p;
if(!L) return ERROR;
p = L->next;
printf((flag++)==1?"\n原表为:\n":"\n处理后:\n");
if(!p) printf("\n表已空\n"); //表已经删空
while(p){
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
Status ListDelete(LinkList &L,int mink,int maxk){//删除
LinkList p,q,t;
q = L; p = L->next; //辅助指针
if(!L || !p) return ERROR;
if(mink >= maxk){
printf("阈值输错\n");
return ERROR;
}
if(p->data >= maxk) return OK; //链表最小值大于maxk
while(p){
if(p->data > mink && p->data < maxk){
t = p;
q->next = p->next;
p = p->next;
free(t);
}
else{
q = p;
p = p->next;
}
}
return OK;
}
int main(){
LinkList L;
int n,mink,maxk;
CreateList(L);
PrintList(L,1);
printf("请输入mink和maxk:\n");
scanf("%d%d",&mink,&maxk);
ListDelete(L,mink,maxk);
PrintList(L,0);
return 0;
}
2.21 试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表(a1,a2, … ,an)逆置为(an,an-1, … ,a1)。
Status ListInverse(SqList &L){
//顺序表就地逆置,不另开空间
if(L.length == 0) return ERROR;
j = L.length;
for(i = 1;i < j;i++)
L.elem[i] <-> L.elem[j--];//两端向中间进行
return OK;
}//ListInverse
完整C实现:(假设所有元素为整型)
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define LIST_INIT_SIZE 100 //初始分配容量
#define LISTINCREMENT 10 //分配增量
typedef int Status;
typedef struct {
int *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配容量
}SqList;
Status InitList(SqList &L){//建表
L.elem = (int*)malloc(LIST_INIT_SIZE*sizeof(int));
if(!L.elem){
printf("建表出错\n");
exit(OVERFLOW);
}
L.length = 0;
L.listsize = LIST_INIT_SIZE;
return OK;
}
Status InputList(SqList &L,int n){//输入
int i;
if(n < 1 || n > L.listsize){
printf("输入出错\n");
return ERROR;
}
printf("请输入元素,以回车隔开:\n");
for(i = 1;i <= n;i++)
scanf("%d",&L.elem[i]);
L.length = n;
return OK;
}
Status OutputList(SqList L,int n,int flag){//输出
int i;
if(n < 1 || n > L.listsize) return ERROR;
if(L.length == 0) return ERROR;
printf((flag++) == 1 ? "\n原表为:\n" : "\n逆置后:\n");
for(i = 1;i <= n;i++)
printf("%d ",L.elem[i]);
return OK;
}
Status ListInverse(SqList &L){//就地逆置
int temp,i,j;
if(L.length == 0) return ERROR;
j = L.length;
for(i = 1;i < j;i++){
temp = L.elem[i];
L.elem[i] = L.elem[j];
L.elem[j--] = temp;
}
return OK;
}
int main(){
SqList L;
int n;
InitList(L);
printf("请输入元素个数:");
scanf("%d",&n);
InputList(L,n);
OutputList(L,n,1);
ListInverse(L);
OutputList(L,n,0);
return 0;
}
2.24 假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结构,请编写算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许表中含有值相同的元素)排列的线性表C,并要求利用原表(即A表和B表)的结点空间构造C。
Status MergeList(LinkList &A,LinkList &B,LinkList &C){
//将两带头结点的非递减单链表AB,合并为带头结点的非递增单链表C,使用原表结点空间。
if(!A || !B || (!A->next && !B->next))
return ERROR;
pa = qa = A; pb = qb = B;
pa = pa->next; pb = pb->next;
A->next = NULL; C = A;//用A的头结点充当C的头结点
while(pa && pb){
if(pa->data < pb->data){//将最小值入A
qa = pa;
pa = pa->next;
qa->next = A->next;
A->next = qa;
}
else{
qb = pb;
pb = pb->next;
qb->next = A->next;
A->next = pb;
}
}//while
while(pa){//如果pa剩余
qa = pa;
pa = pa->next;
qa->next = A->next;
A->next = qa;
}
while(pb){//如果pb剩余
qb = pb;
pb = pb->next;
qb->next = A->next;
A->next = pb;
}
pb = B; free(pb);
return OK;
}//MergeList
完整C实现:
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct LNode {
int data;
struct LNode *next;
}LNode,*LinkList;
Status CreateList(LinkList &L){//建表,带头结点
LinkList p,rear;
int i,n;
L = (LinkList)malloc(sizeof(LNode));
if(!L) return ERROR;
L->data = NULL; L->next = NULL;
rear = L;
printf("请输入元素的个数:");
scanf("%d",&n);
printf("请按照非递减原则,输入元素:\n");
for(i = 0;i < n;i++){
p = (LinkList)malloc(sizeof(LNode));
if(!p) return ERROR;
p->data = NULL; p->next = NULL;
scanf("%d",&p->data);
rear->next = p;
rear = p;
}
rear->next = NULL;
return OK;
}
Status PrintList(LinkList L,int flag){
LinkList p;
if(!L) return ERROR;
p = L->next;
switch(flag){
case 1: printf("表A为:\n");break;
case 2: printf("表B为:\n");break;
case 3: printf("表C为:\n");break;
default:break;
}
while(p){
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
Status MergeList(LinkList &A,LinkList &B,LinkList &C){
LinkList pa,qa,pb,qb;
if(!A || !B || (!A->next && !B->next))
return ERROR;
pa = A; qa = pa;
pb = B; qb = pb;
pa = pa->next;
pb = pb->next;
A->next = NULL;
C = A;//用A的头结点充当C的头结点
while(pa && pb){
if(pa->data < pb->data){//将最小值入A
qa = pa;
pa = pa->next;
qa->next = A->next;
A->next = qa;
}
else{
qb = pb;
pb = pb->next;
qb->next = A->next;
A->next = qb;
}
}//while
while(pa){//如果pa剩余
qa = pa;
pa = pa->next;
qa->next = A->next;
A->next = qa;
}
while(pb){//如果pb剩余
qb = pb;
pb = pb->next;
qb->next = A->next;
A->next = qb;
}
pb = B; free(pb);
return OK;
}//MergeList
int main(){
LinkList A,B,C;
CreateList(A);
CreateList(B);
PrintList(A,1);
PrintList(B,2);
MergeList(A,B,C);
PrintList(C,3);
return 0;
}
2.29 已知A,B和C为三个递增有序的线性表,现要求对A表作如下操作:删去那些既在B表中出现又在C表中出现的元素。试对顺序表编写实现上述操作的算法,并分析你的算法的时间复杂度(注意:题中没有特别指明同一表中的元素值各不相同)。
Status DeleteList(SqList &A,SqList B,SqList C){
if(!A.length) return ERROR;
for(i = 0;i < A.length;i++)
for(j = 0;j < B.length;j++)
if(A.elem[i] == B.elem[j])
for(k = 0;k < C.length;k++)
if(A.elem[i] == C.elem[k]){
for(t = i;t < A.length;t++)
A.elem[t] = A.elem[t + 1];
A.elem[t + 1] = NULL;
--A.length;
--i;
}
}
完整C实现:
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define LIST_INIT_SIZE 100 //初始分配容量
#define LISTINCREMENT 10 //分配增量
typedef int Status;
typedef struct {
int *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配容量
}SqList;
Status InitList(SqList &L){//建表
L.elem = (int*)malloc(LIST_INIT_SIZE*sizeof(int));
if(!L.elem){
printf("建表出错\n");
exit(OVERFLOW);
}
L.length = 0;
L.listsize = LIST_INIT_SIZE;
return OK;
}
Status InputList(SqList &L){//输入
int i,n;
printf("请输入元素个数:");
scanf("%d",&n);
if(n < 1 || n > L.listsize){
printf("输入出错\n");
return ERROR;
}
printf("请按照非递减原则输入元素,以回车隔开:\n");
for(i = 0;i < n;i++)
scanf("%d",&L.elem[i]);
L.length = n;
return OK;
}
Status OutputList(SqList L,int flag){//输出
int i,n;
n = L.length;
if(n < 1 || n > L.listsize) return ERROR;
if(n == 0) return ERROR;
switch(flag){
case 1: printf("\n表A为:\n");break;
case 2: printf("\n表B为:\n");break;
case 3: printf("\n表C为:\n");break;
case 4: printf("\n处理后:\n");break;
default:break;
}
if(!L.elem[0]) printf("\n表已空\n");
for(i = 0;i < n;i++)
printf("%d ",L.elem[i]);
return OK;
}
Status DeleteList(SqList &A,SqList B,SqList C){
int i,j,k,t;
if(!A.length) return ERROR;
for(i = 0;i < A.length;i++)
for(j = 0;j < B.length;j++)
if(A.elem[i] == B.elem[j])
for(k = 0;k < C.length;k++)
if(A.elem[i] == C.elem[k]){
for(t = i;t < A.length - 1;t++)
A.elem[t] = A.elem[t + 1];
A.elem[t + 1] = NULL;
--A.length;
--i;
}
return OK;
}
int main(){
SqList A,B,C;
int n,x;
InitList(A);
InitList(B);
InitList(C);
InputList(A);
InputList(B);
InputList(C);
OutputList(A,1);
OutputList(B,2);
OutputList(C,3);
DeleteList(A,B,C);
OutputList(A,4);
return 0;
}
InitList、InputList、OutputList的时间复杂度为O(n),DeleteList的时间复杂度为O(n4)。空间复杂度均为O(n)。
-回过头来发现,本算法没有利用到A,B,C的非递减性质,且时间复杂度过高,现进行优化:
Status DeleteList_New(SqList &A,SqList B,SqList C){
while(i < A.length && j < B.length && k < C.length){
if(B.elem[j] < C.elem[k]) j++;//推进
else if(B.elem[j] > C.elem[k]) k++;
else{
x = B.elem[j];//找到B,C相同值
while(B.elem[j] == x) j++;
while(C.elem[k] == x) k++;
while(i < A.length && A.elem[i] < x)
A.elem[t++] = A.elem[i++];//移动到待删值
while(i < A.length && A.elem[i] == x) i++;//删除
}
}
while(i < A.length) A.elem[t++] = A.elem[i++];//处理剩余元素
A.length = t;
while(t < i) A.elem[t++] = NULL;
return OK;
}
时间复杂度为O(n),n=Min{A.length, B.length , C.length}。