专业课习题总结(第一部分 线性表)
第一部分 线性表
1.1 顺序存储习题(P19~P20)
- 顺序表逆置(P19/2)
//方法一:
for(int i=0,j=n-1;i<j;i++,j--)
{
int temp=L.data[i];
L.data[i]=L.data[j];
L.data[j]=L.data[i];
}
//方法二:
for(int i=0;i<L.length/2;i++)
{
int temp=L.data[i];
L.data[i]=L.data[L.length-i-1];
L.data[L.length-i-1]=temp;
}
- 删除线性表中所有值为x的数据元素(顺序表)
//用k记录顺序表L中等于x的元素个数。边扫描L边统计k,并将不等于x的元素前移k个位置,最后修改L的长度
void del_x_2(Sqlist &L,int x)
{
int k=0,i=0;
while(i<L.length)
{
if(L.data[i]==x)
k++;
else
L.data[i-k]=L.data[i];
i++;
}
L.length=L.length-k;
}
- 删除(s~t)之间的所有元素,在有序顺序表中。
//找最大,最小的位置,然后for循环,删除
bool Del_s_t2(SqList &L,int s,int t)
{
for(int i=0;i<L.length&&L.data[i]<L.data[s];i++);//找s
if(i>=L.lenght)
return false;
for(int j=i;j<L.length&&L.data[j]<=t;j++);//找t
for(;j<L.length;i++,j++)//删除
L.data[i]=L.data[j];
L.length=i;
return true;
}
- 删除有序顺序表中所有重复的元素
//算法思想:初始时,将第一个元素视为非重复的有序表。之后一次判断后面的元素是否与前面非重复有序表的最后一个元素相同,若相同则继续向后判断,若不同则插入到前面的非重复有序表的最后,直至判断到表尾为止
bool Delet_Same(SeqList& L)
{
if(L.length==0)
return false;
int i,j;
for(i=0,j=1;j<L.length;j++)
if(L.data[i]!=L.data[j])
L.data[++i]=L.data[j];
L.length=i+1;
return true;
}
- 将两个有序顺序表合并为一个新的有序顺序表
//采用归并排序
Bool Merge(SeqList A,SeqList B,SeqList C)
{
if(A.length+B.length>C.maxSize)
return false;
int i=0,j=0,k=0;
while(i<A.length&&j<B.length)
{
if(A.data[i]>=B.data[j])
C.data[k++]=B.data[j++];
else
C.data[k++]=A.data[i++];
}
while(i<A.length)
C.data[k++]=A.data[i++];
while(j<B.length)
C.data[k++]=B.data[j++];
C.length=k;
return true;
}
- A[m+n],存放在两个线性表中,将其逆置
void Reverse(int A[],int left,int right,int arraySize)
{
if(left>=right||right>=arraySize)
return;
int i,j;
for(int i=right,j=left;i<j;i++,j--)
{
int temp=A[i];
A[i]=A[j];
A[j]=A[i];
}
}
void ExChange(int A[],int m,int n,attaySize)
{
Reverse(A,0,n+m-1,arraySize);//整个反转
Reverse(A,0,n-1,arraySzie);//B反转
Reverse(A,n,m+n-1,arraySzie);//A反转
}
1.2 链表(P41~P43)
- 将单链表就地逆置
//算法思想:本质上就是采用头插法,将头结点取出来。
LinkList Reverse_1(LinkList L)
{
LNode *p=L->next;
LNode *q;
L->next=NULL;
while(P!=NULL)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
return L;
}
- 给定两个单链表,编写算法找出两个链表的公共结点。
/*
算法思想:
1、两个单链表出现第一个公共结点之后,后面都重合
2、有表长,表短,要让它们在一个起跑线上
*/
LinkList Search_1st_Common(LinkList L1,LinkList L2)
//本算法实现在线性的时间内找到两个单链表的第一个公共结点
{
int len1=Length(L1),len2=Length(L2);
LinkList longList,shortList;
if(len1>len2)
{
longList=L1->next;
shortList=L2->next;
dist=len1-len2;
}
else
{
longList=L2->next;
shortList=L1->next;
dist=len2-len1;
}
while(dist--)
longList=LongList->next;
while(LongList!=NULL)
{
if(longList==shortList)
return longList;
else
{
longList=longList->next;
shortList=shortList->next;
}
}
return NULL;
}
- 将原表奇数和偶数序号的元素分成两个链表
/*
要点:
1、 用if(i%2==0)来进行判别
2、要建立一个新的链表,B要创建头结点
3、采用尾插法,记得最后尾指针要指向空
*/
- 在一个递增有序的线性表中,存在数值相同的存在。设计算法去除相同数值的元素。
void Del_Same(LinkList &L)
{
LNode *p=L->next,*q;
if(p==NULL)
return;
while(p->next!=NULL)
{
q=p-next;
if(p->data==q->data)
{
p->next=q->next;
free(q);
}
else
p=p->next;
}
}
- 判断序列B是否为序列A的连续子序列。
int Pattern(LinkList A,LinkList B)
{
LNode *p=A;
LNode *pre=p;//用来记住每趟比较中A链表的开始结点
LNode *q=B;
while(p&&q)
{
if(p->data==q->data)
{
p=p->next;
q=q->next;
}
else
{
pre=pre->next;
p=pre;//A链表新的开始比较结点
q=B;//q从B链表第一个结点开始
}
}
if(q==NULL)//B已经结束
return 1;
else
return 0;
}
- 判断循环双链表是否对称
/*
算法思想:让p从左向右扫描,直到它们指向同一结点(p==q,当循环双链表中结点个数为奇数时)或相邻(p->next=q或q->prior=p,当循环双链表中结点个数为偶数时)为止,若它们所指结点值相同,则继续进行下去,否则返回0.若比较全部相等,则返回1.
*/
int Symmetry(DLinkList L)
//本算法从两头扫描循环双链表,以判断链表是否对称
{
DNode *p=L->next,*q=L->prior;
while(p!=q&&q->next!=p)
if(p->data==q->data)
{
p=p->next;
q=q->next;
}
else
return 0;
return 1;
}
第二部分栈和队列
2.1 栈
- 设单链表的表头指针为L,结点结构由data和next两个域组成,其中data域为字符型。试设计算法判断该链表的全部n个字符是否中心对称。例如xyx,xyyc都是中心对称。
//算法思想:使用栈来判断链表中的数据是否中心对称。让链表的前一半元素依次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,若相等,则将链表中的下一个元素与栈中再弹出的元素比较,直至链表到尾。这时若栈是空栈,则得出链表中心对称的结论;否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。
int dc(LinkList L,int n)
{
int i;
char s[n/n];
LNode *p=L->next;
for(i=0;i<n/2;i++)
{
s[i]=p->data;
p=p->next;
}
i--; //恢复最后的i值
if(n%2==1) //如果n为奇数,后移过中心结点
p=p->next;
while(p!=NULL&&s[i]==p->data) //检测是否中心对称
{
i--; //i充当栈顶指针
p=p->data;
}
if(i==-1) //栈为空栈
return 1;
else
return 0;
}
- 设有两个栈s1、s2都采用顺序栈方式,并共享一个存储区[0,…,maxsize-1],为了尽量利用空间,减少溢出的可能,课采用栈顶相向,迎面增长的存储方式。试设计s1、s2有关入栈和出栈的操作算法。
//定义结构体
/*
算法思想:两个栈共享向量空间,将两个栈的栈底设在向量两端,初始时,s1栈顶指针为-1,s2栈顶指针为maxSize。两个栈顶相向、迎面增长,栈顶指针指向栈顶元素。
*/
typedef struct{
int stack[maxsize]; //栈空间
int top[2]; //top为两个栈顶指针
}stk;
stk s;
//入栈操作
/*
s1为通常意义下的栈,而s2栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)
*/
int push(int i,int x)
{
if(i<0||i>1)
{
printf("栈号输入不对");
exit(0);
}
if(s.top[1]-s.top[0]==1)
{
printf("栈已满\n");
return 0;
}
switch(i)
{
case 0: s.stack[++s.top[0]]=x;return 1;break;
case 1: s.stack[--top[1]]=x;return 1;
}
}
//退栈操作
int pop(int i)
{
if(i<0||i>1)
{
printf("栈已满\n");
return 0;
}
switch(i)
{
case 0:
if(s.top[0]==-1)
{
printf("栈空\n");
return -1;
}
else
return s.stack[s.top[0]--];
case 1:
if(s.top[1]==maxsize)
{
printf("栈空\n");
return -1;
}
else
return s.stack[s.top[1]++];
}//Switch
}
2.2 队列
- 将循环队列所有的元素都能够得到利用,则需设置一个标志域tag,并以tag的值为0或1来区分对头指针front和队尾指针rear相同时的队列状态是“空”还是“满”。
/*
算法思想:进队:tag=1;出队:tag=0;
因为只有入队才能导致队满,只有出队才能导致队空
对空条件:Q.front==Q.rear&&Q.tag==0
队满条件:Q.front==Q.rear&&Q.tag==1
进队操作:Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%maxSize;Q.tag=1;
出队操作:x=Q.dara[Q.front];Q.front=(Q.front+1)%maxSize;Q.tag=0;
*/
//入队操作
int EnQueue(SqQueue &Q,int x)
{
if(Q.front==Q.rear&&Q.tag==1)
return 0;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%maxSize;
Q.tag=1;
return 1;
}
//出队操作
int DeQueue(SqQueue &Q,int x)
{
if(Q.front==Q.rear&&Q.tag==0)
return 0;
x=Q.dara[Q.front];
Q.front=(Q.front+1)%maxSize;
Q.tag=0;
return 1;
}
- 将队列中元素逆置
/*
算法思想:一般逆置都是采用的栈,来从中逆置
我们先将队列中元素放入栈中,再从栈中取出来,放入队列中
*/
void Inverser(Stack S,Queue Q)
{
while(!QueueEmpty(Q))
{
x=DeQueue(Q);
Push(S,x);
}
while(!StackEmpty(S))
{
Pop(S,x);
EnQueue(Q,x);
}
}
2.3 栈和队列的应用
- 假设一个算术表达式中包含圆括号,方括号和花括号3种类型的括号,编写一个算法来判断表达式中的括号是否配对,以字符“\0”作为算术表达式的结束符。
bool BraacketsCheck(char *str)
{
InitStack(S);
int i=0;
while(str[i]!='\0')
{
switch(Str[i])
{
//左括号入栈
case '(': Push(S,'(');break;
case '[': Push(S,'[');break;
case '{': Push(S,'{');break;
//遇到右括号,检测栈顶
case ')': Pop(S,e);
if(e!='(') return false;
break;
case ']': Pop(S,e);
if(e!='[') return false;
break;
case '}': Pop(S,e);
if(e!='{') return false;
break;
}//swith
i++;
}
if(!IsEmpty(S))
{
printf("括号不匹配!");
return false;
}
else
{
printf("括号匹配!");
return true;
}
}
- 利用栈实现以下递归函数的非递归计算:
P n ( x ) = { 1 , n = 0 2 x , n = 1 2 x P n − 1 ( x ) − 2 ( n − 1 ) P n − 2 ( x ) , n > 1 P_n\left( x \right) =\begin{cases} 1, &&n=0\\ 2x, &&n=1\\ 2xP_{n-1}\left( x \right) -2\left( n-1 \right) P_{n-2}\left( x \right) , &&n>1\\ \end{cases} Pn(x)=⎩⎪⎨⎪⎧1,2x,2xPn−1(x)−2(n−1)Pn−2(x),n=0n=1n>1
/*
算法思想:设置一个栈用于保存n和对应的Pn(x)值,栈中相邻元素的Pn(x)有题中关系。然后边出栈边计算Pn(x),栈空后该值就计算出来了。
*/
double p(int n, double x)
{
if (n == 0)
return 1;
if (n == 1)
return 2 * x;
if (n > 1)
{
InitStack(S);
double x1 = 1, x2 = 2 * x;
int i = 2;
Push(S, x1);
Push(S, x2);
while (i <= n)
{
x2 = Pop(S, top);
x1 = Pop(S, top);
x1 = 2 * x * x2 + 2 * (i - 1) * x1;
Push(S, x2);
Push(S, x1);
i++;
}
return Push(S, top);
}
}
- 某汽车轮渡口,过江渡船每次能载10辆车过江,过江车辆分为客车类和货车类,上渡船有如下规定:同类车先到先上船;客车先于货车上船,且每上4辆客车,才允许放上1辆货车;若待客车不足4辆,则以货车代替;若无货车等待,允许客车都上船。试设计一个算法模拟渡口管理。
/*
算法思想:
1、“同类车先到先上船”————队列,一个队列负责一种车
2、每次上限是10,也就是4辆客车+1辆货车,4辆客车+1辆货车
3、最后两句话其实就是“没有客车的话,货车可以替代客车;没有货车的话客车可以替代货车”
4、写两个函数,用于上客车和货车,每一个内部再进行判断是否够用
*/
Queue q; //过江渡船载渡队列
Queue q1; //客车队列
Queue q2; //货车队列
void manager()
{
int i=0,j=0;
while(j<10)
{
if(!QueueEmpty(q1)&&i<4) //客车队列不空,且未上足四辆
{
DeQueue(q1,x);
EnQueue(q,x);
i++;
j++;
}
else if(i==4&&!QueueEmpty(q2)) //客车已经上足四辆
{
DeQueue(q2,x);
EnQueue(q,x);
j++;
i=0;
}
else
{
while(j<10&&i<4&&!QueueEmpty(q2)) //其它情况
{
DeQueue(q2,x);
EnQueue(q,x);
i++;
j++;
}
i=0;
}
if(QueueEmpty(q1)&&QueueEmpty(q2))
j=11;
}
}