线性表-习题
今天主要分享的是解题的思路,希望对大家有所帮助。 无论是什么样的习题,都应该先想好解题的思路,这样做题才会事半功倍。😝。
第一题:将2个递增的有序链表合并为⼀一个链表的有序链表; 要求结果链表仍然使⽤用两个链表的存储空间,不另外占⽤用其他的存储空间. 表中不不允许有重复的数据
首先我们想一下思路(只是提供一种解题思路,也可能有更好的思路)。
1.假设有2个有序链表La,Lb。(分别是:{1,2,4},{2,3,4},我们的目的是变成{1,2,3,4})
2.可以声明一个头指针Lc(借用La的),pa,pb分别指向La,Lb中的元素.
3.循环遍历,Lc存贮较小者,然后相等的话,存pa,删除pb。
4.达到其中一个元素尾部,剩余元素直接拼接(因为这是一个有序递增的)
5.最后把Lb释放了。
然后直接上代码:
void mergeLinkList(ListNode *La, ListNode *Lb, ListNode *Lc){
ListNode pa,pb,pc,temp;
pa = (*La)->next;
pb = (*Lb)->next;
*Lc = pc = (*La);
while (pa && pb) {
if (pa->data < pb->data) {
pc->next = pa;
pc = pa ;
pa = pa->next;
}else if (pa->data > pb->data){
pc->next = pb;
pc = pb;
pb = pb->next;
}else{
temp = pb;
pc->next = pa;
pc = pa ;
pa = pa->next;
pb = temp->next;
free(temp);
}
}
pc->next = pa?pa:pb;
free(*Lb);
}
第二题:已知两个链表A和B分别表示两个集合.其元素递增排列列. 设计⼀一个算法,⽤用于求出A与B的交集,并存储在A链表中; 例例如 : La = {2,4,6,8}; Lb = {4,6,8,10}; Lc = {4,6,8}
老规矩,这个时候先自己想下思路:
这个其实和第一题比较相似。
1.同样,La,Lb,Lc.pa,pb,pc,然后Lc借用La的头结点
2.循环遍历,相等的话,取La,
3.pa小于pb的话,pa向后顺移,然后之前结点删掉。
4.pa大于pb的话,pb向后顺移,然后之前结点删掉。
5.达到其中一个的尾部,另一个的尾部也删掉,因为不可能存在伤痛元素了
6.释放Lb
上代码:
void intersectionLinkList(ListNode *La, ListNode *Lb, ListNode *Lc){
ListNode pa,pb,pc,temp;
pa = (*La)->next;
pb = (*Lb)->next;
*Lc = pc = (*La);
while (pa && pb) {
if (pa->data < pb->data) {
temp = pa;
pa = pa->next;
free(temp);
}else if (pa->data>pb->data){
temp = pb;
pb = pb->next;
free(temp);
}else{
pc->next = pa;
pc = pa;
pa= pa->next;
temp = pb;
pb = pb->next;
free(temp);
}
}
//Lb为空,删除非空表La中的所有元素
while (pa) {
temp = pa;
pa = pa->next;
free(temp);
}
//La为空,删除非空表Lb中的所有元素
while (pb) {
temp = pb;
pb = pb->next;
free(temp);
}
pc->next = NULL;
free(*Lb);
}
第三题:设计⼀一个算法,将链表中所有节点的链接⽅方向"原地旋转",即要求仅仅利利⽤用原表的存储空间. 换句话说,要求算法空间复杂度为O(1); 例如:L={0,2,4,6,8,10}, 逆转后: L = {10,8,6,4,2,0};
思路:
1.这个马上想到的是头插法。
2.然后从前向后遍历
这个就直接代码了(看完之后大呼简单😄):
void rotateLinkList(ListNode *La){
ListNode p,q;
p = (*La)->next;
(*La)->next = NULL;
while (p) {
q = p->next;
p->next = (*La)->next;
(*La)->next = p;
p = q;
}
}
第四题:设计⼀一个算法,删除递增有序链表中值⼤大于等于mink且⼩小于等于maxk(mink,maxk是给定的两个参数,其值可以和表中的元素相同,也可以不不同)的所有元素;
兄弟们,思路:
1.首先他会给2个值。
2.我们把 mink~maxk 之间的结点干掉。
3.用mink前面的结点指向maxk之后的结点
4.结束啦
上代码:
void deleteItems(ListNode *La,int mink, int maxk){
ListNode p,q,pre,temp;
pre = (*La);
p= (*La)->next;
//找前面的结点
while (p && p->data < mink) {
pre = p;
p = p->next;
}
//找后面的结点
while (p && p->data <= maxk) {
p = p->next;
}
// 存一波,然后指过去
q = pre->next;
pre->next = p;
//删掉中间的结点
while (q!=p) {
temp = q->next;
free(q);
q = temp;
}
}
第五题:设将n(n>1)个整数存放到⼀一维数组R中, 试设计⼀一个在时间和空间两⽅方⾯面都尽可能⾼高效的算法;将R中保存的序列列循环左移p个位置(0<p<n)个位置, 即将R中的数据由(x0,x1,......,xn-1)变换为 (xp,xp+1,...,xn-1,x0,x1,...,xp-1).例如: pre[10] = {0,1,2,3,4,5,6,7,8,9}, n = 10,p = 3; pre[10] = {3,4,5,6,7,8,9,0,1,2}。
大家不要看到题比较长就比较慌,这道题大多数就是举例,然后让你明白他要表达什么。(写了这么多,他想表达的就是左移)
思路:
1.将一维数组中所有元素逆置。
2.找到n-p-1的位置,分成2个数组。
3.在逆置
(当然,大家可能有更好的办法。也可以多多尝试。我写的应该是最简单。。😄)
代码:
//逆置方法
void Reverse(int *arr , int left, int right){
int temp;
while (left < right) {
temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left ++;
right --;
}
}
//左移方法(主方法)
void leftMove(int *arr, int n, int p){
//全部逆序
Reverse(arr, 0, n-1);
//前面在逆序
Reverse(arr, 0, n-p-1);
//后面在逆序
Reverse(arr, n-p, n-1);
}
第六题:已知⼀一个整数序列列A = (a0,a1,a2,...an-1),其中(0<= ai <=n),(0<= i<=n). 若存在ap1= ap2 = ...=apm = x,且m>n/2(0<=pk<n,1<=k<=m),则称x 为 A的主元素. 例例如,A = (0,5,5,3,5,7,5,5),则5是主 元素; 若B = (0,5,5,3,5,1,5,7),则A 中没有主元素,假设A中的n个元素保存在⼀一个⼀一维数组中,请设 计⼀一个尽可能⾼高效的算法,找出数组元素中的主元素,若存在主元素则输出该元素,否则输出-1.
这个题还是这么长,不慌,慢慢分析。
找到数组中的主元素(个数>数组个数/2)
1.首先临时选取key为一个主元素。遍历数组。找到相同的数,count则加一,否则减一。
2.当count小于0时更换下一个元素为候选主元素。
3.遍历之后并不代表key就是主元素。判断key的count>n/2,成立则是主元素,不成立就没有主元素了。
上代码:
int findMianElement(int *A, int n){
int key = A[0];
int count = 1;
for (int i = 0; i<n; i++) {
if (A[i] == key) {
count ++;
}else{
if (count > 0 ) {
count --;
}else{
key = A[i];
count = 1;
}
}
}
if (count > 0) {
for (int i = count = 0; i<n; i++) {
if (key == A[i]) {
count ++ ;
}
}
}
if (count >n/2) {
return key;
}
return -1;
}
第七题:⽤用单链表保存m个整数, 结点的结构为(data,link),且|data|<=n(n为正整数). 现在要去设计⼀一个时间复杂度尽可能⾼高效的算法. 对于链表中的data 绝对值相等的结点, 仅保留留第⼀一次出现的结点,⽽而 删除其余绝对值相等的结点.例例如,链表A = {21,-15,15,-7,15}, 删除后的链表A={21,-15,-7};
提示要一个时间复杂度尽可能高效的算法。所以我们可以用空间换时间。
思路:
1.申请一个大于n的辅助数组。初始值为0
2.遍历链表。给数组赋值(当然要去绝对值了),这个值出现赋值1。
3.如果这个值等于1,说明不是第一次出现,,删除。
4.如果这个值等于0,说明第一次出现,留下。
上代码:
void deleteEqualABSValue(ListNode *L,int n){
int *p = alloca(sizeof(int)*n);
ListNode r = *L;
//2.数组元素初始值置空
for (int i = 0; i < n; i++) {
*(p+i) = 0;
}
//3.指针temp 指向首元结点
ListNode temp = (*L)->next;
//4.遍历链表,直到temp = NULL;
while (temp!= NULL) {
//5.如果该绝对值已经在结点上出现过,则删除该结点
if (p[abs(temp->data)] == 1) {
//临时指针指向temp->next
r->next = temp->next;
//删除temp指向的结点
free(temp);
//temp 指向删除结点下一个结点
temp = r->next;
}else
{
//6. 未出现过的结点,则将数组中对应位置置为1;
p[abs(temp->data)] = 1;
r = temp;
//继续向后遍历结点
temp = temp->next;
}
}
}
总结
其实解析所有问题都是一样,首先理清思路,然后在根据思路写逻辑。没有人第一眼看下去,啥都不想直接写的(除非他把这道题背下来了。。)。所以想清解题思路超级重要。