每周刷题第二期

 

个人主页:星纭-CSDN博客

系列文章专栏:刷题

踏上取经路,比抵达灵山更重要!一起努力一起进步!

目录

一.选择题

1. 

 2.

二.编程题

1.添加逗号

 方法一:递归

 方法二:迭代

2.删除公共字符

3.返回倒数第k个节点 

4.链表的回文结构 

5.相交链表 

6.随机链表的复制

7.环形链表


写题写题!!!

一.选择题

1. 

#include<stdio.h>
int main(){
 	unsigned char i = 7;
 	int j = 0;
 	for(;i > 0;i -= 3){
 		++j;
 	}
 	printf("%d\n", j);
 	return 0;
}

 输出结果是什么?

char类型的变量8个比特位,范围是-128-127.无符号char类型的变量也是8个比特位,范围是0-255.

当超过255时就会进入下一次循环,比如255+1就是0,同理当小于0是也是如此0-1就是255.

这道题的循环的限制条件是i>0,所以循环结束的时候i=0,当然也可能无法等于0直接死循环。

j每加1.i就减3.

j = 0123....3 + 8488....88+85173
i = 741254....减了84次32255....0

所以最终结果是173. 

 2.

#include<stdio.h>
#include<stdlib.h>
void main()
{
  int a = -3;
  unsigned int b = 2;
  long c = a + b;
  printf("%ld\n", c);
}

这道题与整形提升和算术转化有关,不了解的读者可以看这篇文章末尾: http://t.csdnimg.cn/PNaR2

a与b相加,a首先会进行算术转化为unsigned int 类型。又因为用c来接受结果,所以最后的类型是long,%ld是以表示数据按十进制有符号长型整数输入或输出。

二.编程题

1.添加逗号

 题目来源:添加逗号__牛客网

 方法一:递归

void print(int n){
	if(n < 1000){
		printf("%d",n);
	}
	else{
	print(n/1000);
	printf(",%03d",n%1000);
	}
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	print(n);
	return 0;
}

这道题用递归的方式很容易,假设在位数为n的数字中添加逗号,我们可以拆解为在位数为n-3的数字中添加逗号加上逗号和最后三个数字。

结束条件就是当小于四位的时候直接打印即可。

这里为了避免0没有被打印的情况需要补0.

 方法二:迭代

#include<stdio.h>
#include<string.h>
int main(){
	char arr[11] = { 0 };
	
	gets(arr);
	int i = 0;
	int len = strlen(arr);
	for(i = 0;i < len;i++)
	{
		printf("%c",arr[i]);
		if((len - i - 1) % 3 == 0 && i != len - 1){
		printf("%c",',');
		}
	}
	return 0;
}

观察添加完逗号的数字,我们会发现每一个逗号后面的数字个数都是3的整数倍,所以在我们循环打印的时候,当还剩3的整数倍的数字时就需要打印一个逗号了。注意这里为了避免最后还多一个逗号条件还要加一个不是最后一个数字。

这道题的解法还有很多,比如我们通过模10除10,得到最后一个存入一个字符数组中,每存三位加一个逗号,最后再反向打印出来。

2.删除公共字符

题目出处:删除公共字符_好未来笔试题_牛客网 

 因为所给的字符串是有空格的,正常情况下scanf是不能接受空格的,其实也行。

char arr[10];
scanf("%[^\n]s",arr);
printf(arr);

回到这道题;

首先我们需要接受两个字符串的输入这里我们采用gets函数。

#include<stdio.h>

int isexit(char ch,char* arr){
	int i = 0; 
	for(i = 0;i < strlen(arr);i++)
	{
		if(ch == arr[i])
		{
		return 1;
		}
	}
	return 0;
}
int main(){
	char arr1[100] = {0};
	char arr2[100] = {0};
	gets(arr1);
	gets(arr2);
	int i = 0;
	for(i = 0;i < strlen(arr1) ;i++)
	{
		if(isexit(arr1[i],arr2) == 1)
		{
		;
		}else{
		
			printf("%c",arr1[i]);
		}
	}

	return 0;
}

再逐个的判断字符串一中的字符是否存在于2中如果存在,就跳过不打印,不在就打印出来即可 

3.返回倒数第k个节点 

题目出处:. - 力扣(LeetCode) 

题目描述:实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

解题思路:通过题目描述,我们知道这是一个单向不循环链表,所以链表是不能倒着走的。

最简单的方法就是使用双指针,使这两个指针之间的距离隔开k即可。

可以先让fast指针先走k步,如何二者同时走,直到fast遇到链表NULL,此时的slow指向的就是倒数第k个节点。

 代码:

int kthToLast(struct ListNode* head, int k){
	struct ListNode* fast,*slow;
	fast = slow = head;
	int n = k;
	while(n--){
		fast = fast->next;
	}
	while(fast){
		fast = fast->next;
		slow = slow->next;
	}
	return slow->val;
}

 4.链表的回文结构 

题目出处:链表的回文结构_牛客题霸_牛客网 

题目描述:对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

回文结构:从左往右和从右往左都是一样的。比如数字1221。

解题思路:首先找到这个链表的中间节点,如何将中间节点以后的链表翻转。

然后从两边开始依次比较对称的两个节点是否相等直到NULL,即使是偶数个节点也可以使用这样的方式完成,此时的中间节点就是中间两个节点的第二个。

因为在比较过程中,会有一个指针先行到达NULL,就停止比较了。

在上面的方法中,我们使用了我们之前做过的题的方法。

寻找中间节点和翻转链表,不了解的读者可以观看我的上一篇文章。

寻找中间节点:

    struct ListNode* middleNode(struct ListNode* head) {
        struct ListNode* fast, *slow;
        fast = slow = head;
        while (fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }

翻转链表:

    struct ListNode* reverseList(struct ListNode* head) {
        if (head == NULL || head->next == NULL) {
            return head;
        } else {
            struct ListNode* new_Head = reverseList(head->next);
            head->next->next = head;
            head->next = NULL;
            return new_Head;
        }
  
    }

然后我们就可以依次进行比较对称节点中的值了。 

    bool chkPalindrome(ListNode* A) {
        // write code here
        struct ListNode*mid = middleNode(A);
        struct ListNode*B =  reverseList(mid);
        struct ListNode*cura = A,*curb = B;
        while(cura && curb){
            if(cura->val != curb->val){
                return false;
            }
            cura = cura->next;
            curb = curb->next;
        }
        return true;
    }

 奇数个节点的时候,两个指针同时到底NULL,偶数个时curb会先到,提前停止比较,不会有问题。

5.相交链表 

题目出处:. - 力扣(LeetCode)

题目描述:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

解题思路:我们可以采用两个指针,遍历两个链表,当这两个指针相同的时候,这个节点就相交的节点了,因为这道题说明了不会存在环的情况,就相对简单一点。

关键是:如果这两个指针从头开始,因为距离相交节点的距离不同,可能会出现一个先到一个后到,然后错过的情况,那么如何使这两个指针同时到达这个节点呢?

我们可以让长的那个链表的指针,先走两个链表的节点差距个数。 如图所示:

正如这样两个指针,从相对一样的位置开始,必定同时到达相交节点,我们怎么知道两者到底相差多少个节点呢?首先我们需要知道两个链表的长度。

int LenList(struct ListNode* cur){
	int count = 0;
	while(cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

我们专门封装一个函数来完成求链表长度的功能,因为不成环,所以不会死循环。

然后就是让长链表先走:

	int a = LenList(headA);
	int b = LenList(headB);
	//假设法a>b
	struct ListNode* longlist = headA;
	struct ListNode* shortlist = headB;
	if(b > a){
		longlist = headB;
		shortlist = headA;
	} 
	int gap = a > b? a-b:b-a;
	while(gap--){
		longlist = longlist->next;
	}

 假设法也是解决实际问题的一个好方式,这里使用假设法,可以迅速完成代码,并不需要针对情况写if的条件语句了。

然后就是gap了,因为我们不知道具体谁大谁小,这里使用三目操作符,求绝对值,当然也可以使用abs函数来完成这个求绝对值的操作。

最后就是求这个节点了。

	while(shortlist){
		if(shortlist == longlist){
			return shortlist;
		}
		shortlist = shortlist->next;
		longlist = longlist->next;
	}
	return NULL;

6.随机链表的复制

题目出处:. - 力扣(LeetCode)

题目描述:

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

你的代码  接受原链表的头节点 head 作为传入参数。

这里的意思其实简单来说,就是让你复制出一个与原链表一模一样的链表,这道题的难点,其实只有一个,就是节点的random指针也要相互对应:

解题思路:我们先在原链表中的每一个节点的后面插入一个新的节点,这个新的节点的值与前一个节点相同,然后遍历链表使新节点的random指针指向前一个节点的random指针指向的节点的下一个节点

加粗的这句话,就是解决这道题的关键。

完成了以上操作,然后就将新的节点拿出来,构成一个新的链表,这样就完成了链表的复制。

//创建新节点 
struct Node*BuyNode(int x){
	struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
	newnode->val = x;
	newnode->next = newnode->random = NULL;
	return newnode; 
}
struct Node* copyRandomList(struct Node* head) {
	struct Node*cur = head;
	while(cur){
		struct Node* newnode = BuyNode(cur->val);
	//尾插 
		newnode->next = cur->next;
		cur->next = newnode;
		cur = cur->next->next;
	}
	//random
	cur = head;
	while(cur){
		if(cur->random == NULL){
			cur->next->random = NULL;
		}else{
		cur->next->random = cur->random->next; 
		}
		cur = cur->next->next;
	}
    struct Node* copyHead = NULL,*copyTail = NULL;
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        if(copyTail==NULL)
        {
            copyHead = copyTail = copy;
        }
        else{
            copyTail->next = copy;
            copyTail = copyTail->next;
        }
        cur->next = next;
        cur = next;
    }
    return copyHead;
}

7.环形链表

题目出处:. - 力扣(LeetCode) 

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

解题思路:使用快慢指针,如果存在环,那么fast指针会比slow先进入环中,此时二者是有距离差的,快指针每次走两步,慢指针每次走一步。速度差是1,两者会不断接近,从而相遇,如果每相遇就不是环状的了。

代码:

bool hasCycle(struct ListNode *head) {
    struct ListNode* fast,*slow;
    fast = slow = head;
    while(fast && fast->next){
		fast = fast->next->next;
		slow = slow->next;
		if(slow == fast){
			return true;
		}
	}
	return false;
}
  • 32
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值