第二章算法大题
数组
- 从顺序表中删除具有最小值的元素(唯一)
bool Del_Min(sqList &L,ElemType &value) {
if (L.Length==0) {
return false;
}
value = L.data[0];
int pos=0;
for (int i = 1;i < L.length; i ++) {
i f(L.data[i] < value ) {
value = L.data[i];
pos = i;
}
}
L.data[pos] = L.data[L.length-1];
L.length --;
return true;
}
2.逆置顺序表
void Reverse (Sqlist &L) {
Elemtype temp;
int n = L.length;
for (int i = 0;i < n/2;i ++) {
temp = L.data[i];
L.data[i] = L.data[n-i-1];
L.data[n-i-1] = temp;
}
}
- 删除线性表值为x的元素
void del_x_l(Sqlist &L,Elemtype x) {
int k = 0,i;
for (i = 0;i < L.length;i ++) {
if (L.data[i] !=x) {
L.data[k] = L.data[i];k++;
}
}
L.lenth = k;
}
bool Del_s_t2(Sqlist &L,ElemType s,Elemtype t) {
int i ,j;
if (i = 0;i < L.length && L.data[i] <s ;i ++) ;
if (i >= L.length) return false;
for (j = i;j < L.length &&L.data[j] <= t; j ++) ;
for (;j < L.length;j ++,i ++)
L.data[i] = L.data[j];
L.length = i;
return true;
}
5 删除s-t之间的元素
bool Del_s_t(SqList &L,ElemType s,ElemType t) {
int i,k =0 ;
if (L.length==0||s>=t) return false;
for (i = 0;i <L.length;i ++) {
if (L.data[i] >= s&&L.data[i] <= t) k++;
else L.data[i-k]=L.data[i];
}
L.length -= k;
return true;
}
6 从有序顺序表中删除所有值重复的元素
bool Delete_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;
}
7 将两个有序顺序表合并成新的有序顺序表
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 ++] = A.data[i ++];
else
C.data[k ++] = B.data[j ++];
}
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;
}
8 A [ m + n ] A[m+n] A[m+n] 中 ( a 1 , a 2 , a 3 , … , a m ) (a_1,a_2,a_3,\dots ,a_m) (a1,a2,a3,…,am) 和 ( b 1 , b 2 , b 3 , … , b n ) (b_1,b_2,b_3,\dots,b_n) (b1,b2,b3,…,bn) 将B放在A的前面
typedef int DataType;
void Reverse(DataType A[],int left,int right,int arraySize) {
if (left>=right || right >= arraySize) return;
int mid = (left + right)/2;
for (int i = 0;i <= mid-left;i ++) {
DataType temp = A[left+i];
A[left+i] = A[right - i];
A[right-i]=temp;
}
}
void Exchange(DataType A[],int m,int n,int arraySize) {
Reverse(A,0,m+n-1,arraySize);
Reverse(A,0,n-1,arraySize);
Reverse(A,n,m+n-1,arraySize);
}
9 线性表递增有序,用最少的时间在表中查找数值为x的元素,若找到则将其与后继元素位置相交换。若找不到,则将其插入表中并使得表中元素仍递增有序。
void SearchExchangeInsert(ElemType A[],ElemType x) {
int low = 0,high = n-1,mid;
while (low <= high) {
mid = (low+high)/2;
if (A[mid] == x) break;
else if (A[mid] < x) low = mid + 1; // 1
else high = mid - 1; // 2
}
// 若最后一个元素与x相等,则不存在与其后继交换的操作
if (A[mid] == x&&mid != n-1) {
t = A[mid];A[mid] = A[mid + 1];A[mid + 1] = t;
}
if (low > high) {
// 关于这里为啥最后是 A[high] 是小于x的:
// 跳出循环的那一步应该是 mid == high == low;
// if 是上部 1 处是最后一步,那么A[mid=high=low]<x;
// if 是 2 , 那么A[mid-1=high]<x;
for (i = n-1; i > high;i --) {
A[i+1] = A[i];
}
A[i+1] = x;
}
}
10.【2010年真题】
- 算法的基本设计思想:
可将这个问题视为把数组ab转换成数组ba(a代表数组的前 p 个元素,b 代表数组中余下的 n-p 个元素),先将
a 逆置 得到 a − 1 b a^{-1}b a−1b,再将 b 逆置得到 a 1 b − 1 a^{_1}b^{-1} a1b−1,最后将 a 1 b − 1 a^{_1}b^{-1} a1b−1 逆置得到 ba。
设 Reverse 函数执行将数组元素逆置的操作,对 abcdefg 向左循环移动 3 个位置的过程如下:
Reverse(0,p-1)
Reverse(p,n-1)
Reverse(0,n-1)
注: Reverse中,两个参数分别表示数组中待转换元素的始末位置。
- 使用C语言描述如下
void Reverse(int R[],int from int to) {
int i,temp;
for (i = 0;i <(to-from+1)/2;i ++) {
temp = R[from+i];R[from+i] = R[to-i];R[to-i] = temp;
}
}
void Converse(int R[],int n,int p)
{
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1);
}
11.【2011年真题】
- 算法的基本设计思想
分别求两个升序序列 A,B 的中位数,设为 a 和 b,求序列A、B的中位数过程如下:
①:若a==b,则a或b即为,所求中位数,算法结束。
②:若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等。
③:若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等
在保留的两个升序序列中,重复过程①,②,③,知道两个序列中均只含一个元素时为止。
- 代码
int M_Search(int A[],int B[],int n) {
int s1 = 0,d1 = n-1,m1,s2 = 0,d2 = n-1,m2;
while (s1!=d1||s2!=d2) {
m1 = (s1+d1)/2;
m2 = (s2+d2)/2;
if (A[m1] == B[m2]) return A[m1];
if (A[m1] < B[m2]) {
if ((s1+d1)%2==0) {
s1 = m1;d2 = m2;
} else {
s1 = m1 + 1;
d2 = m2;
}
}
else {
if ((s2+d2)%2 == 0) {
d1 = m1;
s2 = m2;
} else {
d1 = m1;
s2 = m2 + 1;
}
}
}
return A[s1] < B[s2]?A[s1]:B[s2];
}int M_Search(int A[],int B[],int n) {
int s1 = 0,d1 = n-1,m1,s2 = 0,d2 = n-1,m2;
while (s1!=d1||s2!=d2) {
m1 = (s1+d1)/2;
m2 = (s2+d2)/2;
if (A[m1] == B[m2]) return A[m1];
if (A[m1] < B[m2]) {
if ((s1+d1)%2==0) {
s1 = m1;d2 = m2;
} else {
s1 = m1 + 1;
d2 = m2;
}
}
else {
if ((s2+d2)%2 == 0) {
d1 = m1;
s2 = m2;
} else {
d1 = m1;
s2 = m2 + 1;
}
}
}
return A[s1] < B[s2]?A[s1]:B[s2];
}
12.【2014年统考真题】
- 算法的基本设计思想
算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数确认Num
是否是主元素。
算法可分为以下两步:
①、选取候选的主元素。依次扫描所给数组中的每个整数,将第一个遇到的整数Num保存到c中,记录Num
的出现次数为1;若遇到的下一个整数仍等于Num,则计数加一,否则计数减一;当计数减到0时,将遇到的下一个
整数保存到c中,计数重新计为1,开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部
数组元素。
②、判断c中元素是否是真正的主元素。再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;
否则序列中不存在主元素。
- 算法实现
int Majority(int A[],int n)
{
int i,c,count = 1;
c = A[0];
for (i = 1;i < n;i ++) {
if (A[i] == c) count ++;
else
if (count > 0) count --;
else {
c = A[i];count = 1;
}
}
if (count > 0) {
for (i = count = 0;i < n;i ++)
if (A[i] == c) count ++;
}
if (count > n/2) return c;
else return -1;
}
13.【2018年真题】
- 算法的基本设计思想
分配一个用于标记的数组B[n],用来记录A中是否出现了1~n中的正整数,B[0]对应正整数1,B[n-1]对应 正整数n,初始化B中全部为0。由于A中含有n个整数,因此可能返回的值是1n+1,当A中n个数恰好为1n时返回 n+1。当数组A中出现了小于等于0或大于n的值时,会导致1n中出现空余位置,返回结果必然在1n中,因此 对于A中出现了小于等于0或大于n的值,可以不采取任何操作。 算法流程:从A[0]开始遍历A,若0<A[i]<=n,则令B[A[i]-1]=1;否则不做任何操作。对A遍历结束后 开始遍历数组B,若能找到第一个满足B[i]==0的下标i,返回i+1,即为结果,此时说明A中未出现 的最小正整数在1~n之间。若B[i]全部不为0,返回i+1。`
- 算法实现
int findMissMin(int A[],int n)
{
int i,*B;
B = (int *)malloc(sizeof(int)*n);
memset(B,0,sizeof(int)*n);
for (i = 0;i < n;i ++) {
if (A[i] > 0&&A[i]<=n)
B[A[i]-1] = 1;
}
for (i = 0;i < n;i ++)
if (B[i] == 0) break;
return i + 1;
}
14.【2020年真题】
-
算法的基本设计思想
①、使用D_min记录所有已处理的三元组的最小距离,初值为一个足够大的整数。
②、集合S1、S2和S3分别保存在数组A、B、C中。数组的下标变量i=j=k=0,
当i<|S1|<j<|S2|且k<|S3|时,循环执行下面的步骤:
a) 计算(A[i],B[j],C[k])的距离D;
b) 若 D<Dmin,更新Dmin=D
c) 将A[i],B[j],C[k]中的最小值的下标+1;
③、输出Dmin,结束。 -
算法实现
#define INT_MAX 0x7fffffff
int abs_(int a)
{
if (a < 0) return -a;
else return a;
}
bool xls_min(int a,int b,int c)
{
if (a <= b&&a <= c) return true;
return false;
}
int findMinofTrip(int A[],int n,int B[],int m,int C[],int p)
{
int i = 0,j = 0,k = 0,D_min = INT_MAX,D;
while (i < n&& j < m&&k < p&&D_min > 0) {
D = abs_(A[i]-B[j])+abs_(B[j]-C[k])+abs_(C[k]-A[i]);
if (D<D_min) D_min = D;
if(xls_min (A[i],B[j],C[k])) i ++;
else if (xls_min(B[j],C[k],A[i])) j ++;
else k ++;
}
return D_min;
}
链表
- 设计一个递归算法,删除不带头结点的单链表L中的所有值为x的结点
void Del_x(Linklist &L,ElemType x)
{
LNode *p;
if (L == NULL) {
return ;
}
if (L->data == x) {
p = L;
L = L -> next;
free(p);
Del_x(L,x);
} else {
Del_x(L->next,x);
}
}
- 带头结点删除所有值为x
void Del_x_1(Linklist &L,ElemType x)
{
LNode *p = L -> next,*pre = L,*q;
while (p!=NULL) {
if (p -> data != x) {
pre = p;
p = p -> next;
} else {
q = q;
p = p -> next;
pre -> next = p;
free(q);
}
}
}
void Del_x_2(Linklist *L,ElemType x)
{
LNode *p = L -> next, *r = L,*q;
while (P != NULL) {
if (p->data != x) {
r->next = p;
r = p;
p = p->next;
} else {
q = p;
p = p->next;
free(q);
}
}
r->next = NULL;
}
- 设L为带头结点的带链表,实现从头到尾反向输出每个结点的值
void f3(Linklist &L)
{
print(L);
printf("\n");
}
void print(Linklist &L)
{
if (L == NULL) return;
print(L->next);
printf("%d ",L->data);
}
- 编写带头结点的单链表L中删除一个最小值结点的高效算法
LinkList f4(Linklist L)
{
LNode *pre=L,*now=L->next,*ans_pre = L,*ans=L->next;
int Min = INT_MAX;
while (now != NULL) {
if (now->data < Min) {
ans = now;
ans_pre = pre;
Min = now->data;
}
pre = now;
now = now->next;
}
ans_pre->next = ans->next;
free(ans);
return L;
}
- 就地逆置带头结点的单链表-空间复杂度为
O
(
1
)
O(1)
O(1)
方法一
Linklist f5(Linklist L)
{
LNode *now = L->next,*pre=L;
while (now->next != NULL) {
pre->data = now->data;
tmp = now;
tmp->next = pre;
pre = now;
now = now->next;
}
L->next = now;
return L;
}
方法二
LinkList f5_2(Linklist L)
{
/**
* @brief
* 将头结点摘下然后从第一节点开始,
* 依次插入到头结点的后面(头插法)
*/
LNode *p,*r;
p = L->next;
L->next = NULL;
while (p != NULL) {
r = p->next;
p->next = L->next;
L->next = p;
p = r;
}
return L;
}
- 使带头结点的单链表L中的元素递增有序
void f6(Linklist &L)
{
/**
* @brief
* 想法:遍历链表,将链表中的数据放到堆里面,建堆时间O(n)。
* 然后扫描重建链表
* 时间复杂度O(n),空间复杂度O(n)
*/
LNode *p = L->next,*pre;
LNode *r = p->next;
p->next = NULL;
p = r;
while (p != NULL) {
r = p->next;
pre = L;
while (pre->next!=NULL&&pre->next->data < p->data)
{
pre = pre->next;
}
p->next = pre->next;
pre->next = p;
p = r;
}
}
- 删除元素
void f7(Linklist &L,int min,int max)
{
LNode *pr = L,*p = L->link;
while (p != NULL) {
if (p->data > min&&p->data < max) {
pr->link = p->link;
free(p);
p = pr->link;
} else {
pr = p;
p = p->link;
}
}
}
- 找出两个链表的公共结点
自己的方法
LNode f8(Linklist A,Linklist B)
{
/**
* @brief
* 公共结点之后的全部一样了应该是,所以逆置,倒着找。
*/
Reverse(A),Reverse(B);
//逆置函数
LNode *nowA=A->next,*nowB=B->next;
while (nowA!=NULL&&nowB!=NULL) {
if (nowA == nowB) {
return nowA;
} else {
nowA = nowA->nxet;
nowB = nowB->nxet;
}
}
return NULL;
}
答案
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 == shortList) {
if (longList != NULL) {
if (longList == shortList) {
return longList;
} else {
longList = longList->next;
shortList = shortList->next;
}
}
}
}
return NULL;
}
- 给定带头结点的单链表,按递增顺序输出单链表各节点的数据元素
答案
对链表进行遍历,在每次遍历中找出整个链表的最小值袁术,输出并释放结点所占空间;再查找次小值元素,输出并释放空间,如此下去,直至链表为空,最后释放头结点所占存储空间。
- 分解单链表A,使得A中含有原表中序号为奇数的元素,而B表中含有其他元素
LinkList f10(Linklist A)
{
Linklist B = (Linklist)malloc(sizeof(LNode));
//Linklist B = new Linklist;
int cnt = 0;
LNode *nowa = A->next,*pre = A,*nowb = B;
while (now != NULL)
{
cnt ++;
if (cnt % 2 == 0) {
nowb->next = nowa;
nowb = nowb->next;
LNode *p = nowa;
nowa = nowa->next;
pre = p;
free(p);
} else {
pre = nowa;
nowa = nowa->next;
}
}
}
答案
LinkList DistCreat_1(LinkList &A)
{
int i = 0;
LinkList B = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
LNode *ra = A,*rb = B,*p;
p = A->next;
A->next = NULL;
while (p!=NULL) {
i ++;
if (i%2) {
ra->next = p;
ra = p;
} else {
rb->next = p;
rb = p;
}
p = p->next;
}
ra->next = NULL;
rb->next = NULL;
return B;
}
- 将C=
{
a
1
,
b
1
,
a
2
,
b
2
…
a
n
,
b
n
}
\{a_1,b_1,a_2,b_2\dots a_n,b_n\}
{a1,b1,a2,b2…an,bn}拆分成A和B,使得
A
=
{
a
1
,
a
2
,
…
,
a
n
}
A=\{a_1,a_2, \dots ,a_n\}
A={a1,a2,…,an},
B
=
{
b
1
,
b
2
,
…
,
b
n
}
B=\{b_1,b_2, \dots ,b_n\}
B={b1,b2,…,bn}
自己写的
void f11(Linklist C)
{
Linklist A = NULL,B = NULL;
LNode *now = hc->next,*nowa = A,*nowb = B;
int x = 0;
while (now!=NULL) {
if (!x) {
nowa->next=now;
nowa = now;
} else {
nowb = B->next;
B->next = now;
B->next->next = nowb;
}
now = now->next;
x~=x;
}
答案-这个推荐看一下
LinkList DisCreat_2(LinkList &A)
{
LinkList B = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
LNode *p = A->next,*q;
LNode *ra = A;
while (p != NULL)
{
ra->next = p;ra = p;
p = p->next;
if (p != null) {
q = p->next;
p->next = B->next;
B->next = p;
p = q;
}
}
ra->next = NULL;
return B;
}
- 去掉数值相同的袁术,使得表中不再有重复的元素
void f12(Linklist L)
{
LNode *now = L->next,*q;
if (now == NULL) return ;//自己写的时候忘了
while (now->next != NULL) {
q = now->next;
if (now->data == q->data) {
now->next = q->next;
free(q);
} else {
now = now->next;
}
}
}
- 假设有两个递增线性表,合并成一个递减的单链表,要求利用原来的结点
void f13(Linklist A,Linklist B)
{
LNode *nowa=A->next,aa=A,*nowb=B->next,bb=B;
Linklist *C=NULL,*hc = NULL;
while (nowa!=NULL&&nowb!=NULL) {
if (nowa-data<nowb->data) {
aa = nowa;
aa->next = C->next;
C->next = aa;
nowa = nowa->next;
} else {
bb = nowb;
bb->next = C->next;
C->next = bb;
nowb = nowb->next;
}
}
//这里忘了,当时写的时候就记得有个忘了
if (nowa) nowb=nowa;
while (nowb) {
hc = nowb->next;
nowb->next=C->next;
C->next=nowb;
nowb = hc;
}
free(B);
free(A);
- 从递增有序的A和B中的公共元素产生单链表C,要求不破坏A、B的结点
void f14(Linklist A,Linklist B)
{
LNode *nowa=A->next,*nowb=B->next;
Linklist C=NULL;LNode *hc = NULL;
C->next = hc;//这里忘了写了
while (nowa!=NULL&&nowb!=NULL) {
if (nowa->data==nowb->data) {
LNode tmp=new LNode;
tmp->data = nowa->data;
hc->next = tmp;
hc = hc->next;
delete(tmp);
nowa=nowa->next;
nowb=nowb->next;
} else if (nowa->data<nowb->data) {
nowa=nowa->next;
} eles {
nowb=nowb->next;
}
}
C->next = NULL;//这里忘了写了
}
- 求递增有序的A和B的交集,存放于A链表
Linklist f15(Linklist &A,Linklist &B)
{
//忘了释放结点了
LNode *nowa = A->next,*pre = A,*nowb = B->next;
while (nowa!=NULL&&nowb!=NULL) {
if (nowa->data==nowb->data) {
pre = nowa;
nowa = nowa->next;
u = nowb;
nowb = nowb->next;
free(u)
} else if (nowa->data > nowb->data) {
u = nowb;
nowb = nowb->next;
free(u);
} else {
pre->next = nowa->next;
LNode *tmp = nowa;
nowa = nowa->next;
free(tmp);
}
}
while (nowb) {
u = nowb;
nowb = nowb->next;
free(u);
}
}
- 判断序列B是否是序列A的连续子序列
自己的答案
bool f16(Linklist A,Linklist B)
{
bool flag = false;
LNode *nowa=A->next;
while (nowa != NULL) {
LNode *tmpa = nowa,*tmpb = B->next;
while (tmpa!=NULL&&tmpb!=NULL&&tmpa->data==tmpb->data) {
tmpa = tmpa->next;
tmpb = tmpb->next;
}
if (tmpa == NULL&&tmpb == NULL) {
return true;
}
nowa = nowa->next;
}
return false;
}
答案
bool Pattern(LinkList A,LinkList B)
{
LNode *p=A,*pre = p,*q = B;
while (p&&q) {
if (p->data == q->data) {
p = p->next;
q = q->next;
} else {
pre = pre->next;
p = pre;
q = B;
}
}
if (q == NULL) return true;
else return false;
}
- 判断带头结点的循环双链表是否对称
bool f17(DLinklist L)
{
DNode *p = L->next,*q = L->prior;
while (p != q&&p->next != q) {
if (p -> data == q -> data) {
p = p->next;
q = q->prior;
} else {
return 0;
}
}
return 1;
}
- 编写一个函数将其中一个循环单链表连接到另一个之后
Linklist f18(LinkList &h1,LinkList &h2)
{
LNode *p,*q;
p = h1;
while (p->next != h1)
p = p->next;
q = h2;
while (q->next != h2)
q = q->next;
p->next = h2;
q->next = h1;
return h1;
}
- 反复找出循环单链表中结点值最小的结点并输出,然后将该结点从中删除
void f19_Del_All(LinkList &L)
{
LNode *p,*pre,*minp,*minpre;
while (L->next != L) {
p = L->next;pre = L;
minp = p;minpre = pre;
while (p != L) {
if (p->data < minp->data) {
minp = p;minpre = pre;
}
pre = p;
p = p->next;
}
printf("%d",mnip->data);
minpre->next = minp->next;
free(minp);
}
free(L);
}
20.【重点题】
设头指针为L的带头结点的非循环双向链表,其中每个结点有前驱指针和后继指针和数据域,还有一个访问频度域freq。在链表被启用前,其值均初始化为零。每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中的freq域的值增加1,并使此链表中结点保持按访问频度非增的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表头。
DlinkList f20(DLinkList &L,ElemType x)
{
DNode *p = L->next,*q;
while (p && p->data!=x) {
p = p->next;
}
if (!p)
exit(0);
else {
p->freq++;
if (p->pre == L||p->pre->freq > p->freq)
return p;
if (p->next != NULL) p->next->pre = p->pred;
p->pred->next = p->next;
q = p->pred;
while (q != L && q->freq <= p->freq) {
q = q->pred;
}
p->next = q->next;
if (q->next != NULL) q->next->pred = p;
q->next = p;
}
return p;
}
-
【2009年真题】
题目:不改变带有头指针list的链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点。- 算法设计思想
定义两个指针变量p和q,初始时均指向头结点的下一个结点(链表的第一个结点),p指针沿链表移动;当p指针移动到第k个结点时,q指针开始于p指针同步移动;当p指针移动到最后一个结点时,q指针所示结点为倒数第k个结点
- 算法实现步骤
直接看代码吧
- 算法实现
typedef int ElemType;
typedef struct LNode{
ElemType data;
struct LNode *link;
}LNode,*Linklist;
int Search_k(Linklist list,int k)
{
LNode *p = list->link,*q = list->link;
int count = 0;
while (p != NULL) {
if (count < k) {
count ++;
} else {
q = q->link;
}
p = p->link;
}
if (count < k)
return 0;
else {
printf("%d",q->data);
return 1;
}
}
- 【2012年真题】
题目:设计一个时间上尽可能高效的算法,找出str1和str2所指向两个链表共同后缀的起始位置
-
算法的基本设计思想
1.分别求出两个链表的长度m和n
2.将两个链表对齐
3.反复将指针p和q同步向后移动
-
算法实现
typedef struct Node {
char data;
struct Node *next;
}SNode;
int listlen(SNode *head)
{
int len = 0;
while (head->next!=NULL) {
len ++;
head = head->next;
}
return len;
}
SNode* find_addr(SNode *str1,SNode *str2)
{
int m,n;
SNode *p,*q;
m = listlen(str1);
n = listlen(str2);
for (p = str1;m>n;m--)
p = p->next;
for (q = str2;m<n;n--)
q = q->next;
while (p->next!=NULL&&p->next !=q->next) {
p = p->next;
q = q->next;
}
return p->next;
}
- 【2015年真题】
题目:单链表保存m个整数,且 |data| <= n。现要求一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。
- 算法基本设计思想:
使用辅助数组记录链表中已出现的数值,从而只需对链表进行一趟扫描
- 使用C语言描述的单链表结点的数据类型定义
typedef struct node { int data; struct node *link; }NODE; typedef NODE *PNODE;
- 算法实现
void func (PNODE h,int n){
PNODE p = h,r;
int *q,m;
q = (int *)malloc(sizeof(int)*(n+1));
for (int i = 0;i < n+1;i ++)
*(q+i) = 0;
while (p->link!=NULL)
{
m = p->link->data>0?p->link->data:-p->link->data;
if (*(q+m) == 0) {
*(q+m)=1;
p = p->link;
} else {
r = p->link;
p->link = r->link;
free(r);
}
}
free(q);
}
- 【2019年真题】
题目:设计一个空间复杂度为 O(1) 且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表 L’=( a 1 , a n , a 2 , a n − 1 , a 3 , a n − 2 a_1,a_n,a_2,a_{n-1},a_3,a_{n-2} a1,an,a2,an−1,a3,an−2…)
-
算法基本设计思想
1.先找出链表L的中间结点,为此设置两个指针p和q,指针p每次走一步,指针q每次走两步,当指针q到达链尾时,指针p正好在链表的中间结点;2.然后将L的后半段结点原地逆置;3.从单链表前后两段中依次各取一个结点,按要求重排。
-
算法实现
void change_list(NODE *h)
{
NODE *p,*q,*r,*s;
p = q = h;
while (q->next != NULL) {
p = p->next;
q = q->next;
if (q->next != NULL) q = q->next;
}
q = q->next;
p->next = NULL;
while (q != NULL) {
r = q->next;
q->next = p->next;
p->next = q;
q = r;
}
s = h->next;
q = p->next;
p->next = NULL;
while (q != NULL) {
r = p->next;
q->next = s->next;
s->next = q;
s = q->next;
q = r;
}
}