一.两个递增有序链表合并成一个有序链表,要求新的链表使用这两个链表的内存,不占用新的内存空间,并且没有重复数据
分析:设两个链表A{1,3,5,6,7,9},B{2,3,6}。合成后:C{1,2,3,5,6,7,9}。不使用新的内存空间,那么C的头结点需要使用A或B的头结点,进入循环:
A:1<B:2------head->1
A:3>B:2------head->1->2
A:3=B:3------head->1->2->A:3 (B:3没用了,释放掉)
A:5<B:6------head->1->2->A:3->5
A:6=B:6------head->1->2->A:3->5->A:6 (B:6没用了,释放掉)
B循环结束----head->1->2->A:3->5->A:6->7->9
循环结束,B的头指针没有用了,释放掉。
为了记住A和B循环到哪里了,需要声明两个临时变量来记录:tempA
,tempB
;
为了记住C的最后一个结点,需要声明一个临时变量来记录:tempC
;
为了释放重复结点,需要声明一个临时变量记录:temp
。
int mergeList(LinkList *A,LinkList *B, LinkList *C){
LinkList tempA, tempB, tempC, temp;
tempC = *C = *A;//使用A的头结点
tempA = (*A)->next;
tempB = (*B)->next;
while (tempA && tempB) {
if (tempA->data == tempB->data) {
tempC->next = tempA;
tempA = tempA->next;
temp = tempB;
tempB = tempB->next;
tempC = tempC->next;
free(temp);
}else if (tempA->data < tempB->data){
tempC->next = tempA;
tempA = tempA->next;
tempC = tempC->next;
}else {//>
tempC->next = tempB;
tempB = tempB->next;
tempC = tempC->next;
}
}
printf("循环结束");
if (tempA == NULL) {
tempC->next = tempB;
}else {
tempC->next = tempA;
}
free(*B);
return OK;
}
以上算法时间复杂度:
O(n)
,n
是较短的链表的长度。
二. 两个递增有序链表A和B,求出A和B的交集存储在A链表中
分析:假设A{1,3,5,6,7,9},B{2,3,6}。交集为A{3,6}。
进入循环:作比较,释放较小的,相等,释放B的。
A:1<B:2------释放1
A:3>B:2------释放2
A:3=B:3------head->B:3 (B:3没用了,释放掉)
A:5<B:6------释放5
A:6=B:6------head->3->A:6 (B:6没用了,释放掉)
B循环结束----A剩余的结点一定不存在和B的交集,释放
循环结束,B的头指针没有用了,释放掉。
为了记住A和B循环到哪里了,需要声明两个临时变量来记录:tempA
,tempB
;
为了记录新的链表,需要声明一个临时变量:sameList
;
为了释放不重复的结点,需要声明一个临时变量记录:temp
。
Status GetSame(LinkList *A,LinkList *B) {
LinkList tempA,tempB,sameList,temp;
tempA = (*A)->next;
tempB = (*B)->next;
sameList = (*A);
while (tempA && tempB) {
if (tempA->data == tempB->data) {
sameList->next = tempA;
sameList = sameList->next;
tempA = tempA->next;
temp = tempB;
tempB = tempB->next;
}else if (tempA->data < tempB->data){
temp = tempA;
tempA = tempA->next;
free(temp);
}else{//>
temp = tempB;
tempB = tempB->next;
free(temp);
}
}
sameList->next = NULL;
if (tempA) {
while (tempA) {
temp = tempA;
tempA = tempA->next;
free(temp);
}
}
if (tempB) {
while (tempB) {
temp = tempB;
tempB = tempB->next;
free(temp);
}
}
free(*B);
return OK;
}
以上算法时间复杂度:
O(n)
,n
是较长的链表的长度。
三.设计一个算法将链表的所有结点的连接方向原地旋转,要求不开辟额外的内存空间
分析:链表A{1,2,3,4,5,6,7}。旋转后A{7,6,5,4,3,2,1}。
使用头插法创建链表链表时,链表顺序是逆序的,可以使用头插法的思路。不能开辟新的空间,所以依然使用链表A的头指针。
临时变量q
记录旧的链表,p
记录从旧链表中摘下的结点,用头插法将p
插入到新的链表A
中。过程如图:
Status translateList (LinkList *A) {
LinkList p,q;
p = (*A)->next;
(*A)->next = NULL;
while (p) {
q = p->next;
p->next = (*A)->next;
(*A)->next = p;
p = q;
}
return OK;
}
四. 设计算法,删除递增有序链表中值大于等于minK且小于等于maxK的所有元素。(minK和maxK是给定的两个参数,可以和表中元素相同,也可以不同)
分析:链表A{1,2,3,4,5,6,8}删除>=3且<=7的结点之后为A{1,2,8}。
循环找到要删除的第一个结点之前的结点q,并记录要删除的第一个结点deletList,继续循环找到要删除的最后一个结点p,让q指向p的下一个结点完成新的链表。释放删除的结点。
Status DeletNodes(LinkList *A, int minK, int maxK){
LinkList deletList = NULL, p, q;
p = q = (*A)->next;
while (p->next) {
if (p->next->data >= minK && p->next->data < maxK) {
deletList = p->next;
q = p;
break;
}
p = p->next;
}
while (p->next) {
if (p->next->data > maxK) {
q->next = p->next;
p->next = NULL;
break;
}else if (p->next->data == maxK) {
p = p->next;
q->next = p->next;
p->next = NULL;
break;
}
p = p->next;
}
if (deletList) {
LinkList temp;
p = deletList->next;
while (p) {
temp = p;
p = p->next;
free(temp);
}
free(deletList);
}else {
return -1;
}
return OK;
}
五. 设将n(n>1)个整数存放到一维数组R中, 将R中保存的序列循环左移p个位置(0<p<n), 即将R中的数据由(x0,x1,......,xn-1)变换为 (xp,xp+1,...,xn-1,x0,x1,...,xp-1).设计算法要求时间和空间两方面都尽可能的高效
分析:假设R=[1,2,3,4,5,6,7,8,9]。p=3,移动之后为:R= [4,5,6,7,8,9,1,2,3]。
第一步:[1,2,3,4,5,6,7,8,9]
第二步:[9,8,7,6,5,4] [3,2,1]
第二步:[4,5,6,7,8,9] [1,2,3]
void listChange(int *R, int left, int right) {
int i = left, j = right;
int temp;
while (i<j) {
temp = R[i];
R[i] = R[j];
R[j] = temp;
i++;
j--;
}
}
Status arrayTranslate(int *R, int p, int n) {
if (p<n && p>0) {
listChange(R, 0, n-1);
listChange(R, 0, n-p-1);
listChange(R, n-p, n-1);
}
return OK;
}
以上算法时间复杂度:
O(n)
,空间复杂度O(1)
六.⽤单链表保存m个整数, 结点的结构为(data,link),且|data|<=n(n为正整数). 现在要去设计一个时间复杂度尽可能高效的算法. 对于链表中的data绝对值相等的结点, 仅保留第一次出现的结点,而删除其余绝对值相等的结点.
分析:A{2,-3,4,3,15,-15,3}处理后:A{2,-3,4,15}
---------R:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
A:2----- R:[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]
A:-3---- R:[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0]
A:4----- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0]
A:3----- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0]---R[3]位置已经是1,这个节点删除
A:15---- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,15,0]
A:-15--- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,15,0]---R[15]位置已经是1,这个节点删除
A:3----- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0]---R[3]位置已经是1,这个节点删除
Status deletSameNode(LinkList *A, int n) {
int *R = malloc(sizeof(int)*(n+1));
for (int i = 0; i<n; i++) {
R[i] = 0;
}
LinkList p = (*A);
LinkList temp = (*A)->next;
int absValue;
while (temp) {
absValue = abs((temp->data));
if (R[absValue] == 0) {
R[absValue] = 1;
p = p->next;
temp = temp->next;
}else {
//删除这个结点
p->next = temp->next;
free(temp);
temp = p->next;
}
}
return OK;
}
以上算法时间复杂度:
O(m)
,m
是链表的长度。空间复杂度:O(n)
,n
是链表中最大的值。
七.A中的n个元素保存在⼀个⼀维数组中,请设计一个尽可能高效的算法,找出数组元素中的主元素,若存在主元素则输出该元素,否则输出-1.
- 概念解释:主元素-数组中出现次数大于元素总数一半的元素。例如:A[2,1,2,3,2,4,2,5,2,6,2]中2是A的主元素,A[1,2,3,2,4,2,5,2,6,2]中没有主元素。
分析:如果A中一个11个元素,元素X出现了6次,那么就有5个其他元素,让1个X和一个其他元素结为一对。那么最后会剩下一个单独的元素,而且一定是X。再判断X出现的次数是否大于总元素个数的一半,我们就知道了X是否是主元素。
int findMainElement(int *A, int n){
int count = 1;
int key = A[0];
for (int i = 1; i<n; i++) {
if (A[i] == key) {
count++;
}else {
if (count>0) {
count--;
}else {
key = A[i];
count = 1;
}
}
printf("count=%d,key=%d\n",count,key);
}
int num = 0;
if (count > 0) {
for (int i = 0; i<n; i++) {
if (A[i] == key) num++;
}
if (num > n/2) {
printf("主元素是:%d\n",key);
return key;
}
}
printf("没有主元素\n");
return -1;
}
以上算法时间复杂度:
O(n)
。空间复杂度:O(1)
。