周练--C

目录

一.力扣27-移除元素

1.题目

2.思考 

3.代码

3.代码2

二.力扣88-合并两个有序数组

1.题目

2.想法:

3.代码 

三.剑指offer交换奇偶数的位置

1.题目

2. 分析

3.代码 

 四.剑指offer56题

1.tip:

2.代码: 

五.剑指offer---计算一个整数在计算机存储的二进制位中1的个数

代码1

代码2

六.力扣-消失的数字 

思路

七.循环右移

八.力扣206-反转链表

1.题目

2. 代码

1--头插

3.递归---整个链表反转

 3.1递归--前n个结点反转

3.2反转部分链表—递归92

 3.2.1-循环头插--反转部分链表92

九.力扣876-链表的中间节点 (要求遍历一次)

1.题目 

2.图解 

十.链表中第K个结点

tips:

代码

十一.分割链表

1.题目

2.图解​编辑

 3.代码

十二.回文链表

1.tips

2.代码

十三.160相交链表

十四.141.2环形链表

十五.快慢指针


一.力扣27-移除元素

1.题目

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-element
 

2.思考 

周练--C_第1张图片这是思路一

思路二:以空间换时间:不是val的数据放在新数组里,再把新数组的只拷贝过去(不符合题意);空间复杂度为O(n)

思路三用双指针:此时时间复杂度为O(N),空间复杂度为O(1)

1.src位置不是val就放到dst位置然后src++,dst++;

2.src位置是val,src++;

3.在src往后遍历过程中,在2的前提下如果src!=val,就让src所指的元素移到dst所指向的位置周练--C_第2张图片

3.代码

int removeElement(int* nums, int numsSize, int val)
{
    int src =0;
    int dst=0;
    while(src

3.代码2

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:

周练--C_第3张图片

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode*prev=NULL;
    struct ListNode*cur=head;
    while(cur)
    {
        //1.是头部节点
        //2.删除中间元素
        if(cur->val==val)
        {
            if(cur==head)
            {
                head=cur->next;//如果直接先free,会把这个位置的值和next变成随机值
                free(cur);
                cur=head;
            } 
            else
             {
            
             prev->next=cur->next;
             free(cur);
             cur=prev->next;
        
       
             }
        }
        else
        {
            prev=cur;
            cur=cur->next;
        
        }
    }
    return head;
}

二.力扣88-合并两个有序数组

1.题目

 周练--C_第4张图片

周练--C_第5张图片

2.想法:

nums1和nums2都从后往前走,取大的从后往前放(演示过程如下视频)如果从前往后覆盖,很有可能将num1前面的元素弄没。 也可以从后往前覆盖而后排序,但是效率会变低。。 会出现下面情况 周练--C_第6张图片

时间复杂度为O(m+n),空间复杂度为O(1)

15-03-16

3.代码 

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    int end1=m-1;
    int end2=n-1;
    int end=m+n-1;
    while(end1>=0&& end2>=0)
    {
        if(nums1[end1]>nums2[end2])
        {
            nums1[end]=nums1[end1];
            --end;
            --end1;
        }
        else
        {
            nums1[end]=nums2[end2];
            --end;
            --end2;
             
        }
    }
    //如果是nums1没完不需要处理,因为就是归于nums1里面
    while(end2>=0)
    {
        nums1[end]=nums2[end2];
            --end;
            --end2;
             

    }


}

三.剑指offer交换奇偶数的位置

1.题目

周练--C_第7张图片

2. 分析

如果left为偶数,right为奇数,则交换两数的位置(其中有3处易错点已用注释标出)

3.代码 

#include
void swap(int* arr, int sz)
{
	int left = 0;
	int right = sz - 1;
	while (left < right)
	{
		while ((left < right) && (arr[left] % 2 == 1))//如果arr数组中全是奇数,虽然不会报错但是你会越界访问!!!
		{
			left++;
		}
		while ((left

 四.剑指offer56题

一组数据中只有一个数字出现了一次,其他所有数字都是成对出现的,请找出这个数字采用位运算(同0异1)

1.tip:

 ^ 小知识复习:

0与任何一个数字异或的结果都等于这个数;两个相同的数字异或的结果为0;

eg: 1 2 3 1 2 五个数字进行异或,结果为 3;

2.代码: 

#include
int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4 };
	int ret = 0;
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		ret ^= arr[i];
	}
	
	return 0;
}

五.剑指offer---计算一个整数在计算机存储的二进制位中1的个数

*这道题需要注意负整数哦

代码1

(这样子写在当年的谷歌面试中并未得到欣赏,但是我可能还想不出来这个"......")

#include

int main()
{
	int a = -1;//-1在计算机中的补码是32个1哦
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		
		if ((1 & (a >> i)) == 1)
			count++;
		
	
	}
	printf("%d", count);

	return 0;
}

代码2

分析:以15举例(&是同1异0)

num num-1

num&(num-1)  结果

赋值给下一个num

num中1的个数
1111 1110 1110 4
1110 1101 1100 3
1100 1010 1000 2
1000 0111 0000 1
#include
int main()
{
	int a = -1;
	int count = 0;
	while (a)
	{
		a = a & (a - 1);
		count++;
	}
	printf("%d", count);
	return 0;
}

3.或者就是写一个函数,参数中改为(unsigned int a)

六.力扣-消失的数字 

思路

1.给定一个x的值为0,让他与[0,n]中所有的数字异或,再和数组中的每个值进行异或,最后所得到的值就是消失的数字;(回到题目四)//注意:[0,n]是n+1个数字哦

2.(0+...n)-(arr[0]+...arr[n-1])   时间复杂度O(n),空间复杂度O(1);

3.。。。 

七.循环右移

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

1.暴力求解---循环k次---O(n*k)(可以看看左旋数组-代码整合那个博客,思路是一样的)

2.额外开辟空间

3.左逆序右逆序全部逆序----可以做到以O(1)的空间复杂度 注意栈溢出的那点哦

void rotate(int* nums, int numsSize, int k)
{
    if(k>numsSize)//没有这步的话会栈溢出
    {
        k=k%numsSize;
    }
    Reverse(nums,0,numsSize-k-1);
    Reverse(nums,numsSize-k,numsSize-1);
    Reverse(nums,0,numsSize-1);
}
void Reverse(int*nums,int left,int right)
{
    while(left

八.力扣206-反转链表

1.题目

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

周练--C_第8张图片

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

周练--C_第9张图片

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]
提示:
  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000
  • 进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

2. 代码

1--头插

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode*cur=head;
    struct ListNode*newNode=NULL;
    while(cur)
    {
        struct ListNode*next=cur->next;
        //头插
        cur->next=newNode;
        newNode=cur;
        //迭代
        cur=next;
    }
    return newNode;
}

2-迭代

//n1--prev,n2--cur,n3--next便于迭代,不然n1,n2之间的链表断开,怎末走到下一个
//反转的终止条件是n2为空
struct ListNode* reverseList(struct ListNode* head)
{
    
  if(head==NULL)//如果没有这步,下面代码中n2=head,n3=head->next就会出现空指针报错的情况
    {
        return NULL;
    }
    struct ListNode*n1,*n2,*n3;
    n1=NULL;
    n2=head;
    n3=head->next;
    while(n2)
    {
        //核心逻辑-反转
        n2->next=n1;
        //迭代
        n1=n2;
        n2=n3;
        if(n3)//如果没有这步会出现空指针报错
            n3=n3->next;

    }
    return n1;
}

3.递归---整个链表反转

周练--C_第10张图片

1.结束条件:只有一个结点或者没有结点;

2.处理第一个节点与子链表翻转后的头结点

3.返回最终的节点

周练--C_第11张图片

 3.1递归--前n个结点反转

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* successor = NULL; // 后驱节点
// 反转以 head 为起点的 n 个节点
struct ListNode*reverseN(struct ListNode* head, int n) {
    if (n == 1) 
    { 
        // 记录第 n + 1 个节点
        successor = head->next;
        return head;
    }
    // 以 head.next 为起点,需要反转前 n - 1 个节点
    struct ListNode* last = reverseN(head->next, n - 1);
    head->next->next = head;
    head->next = successor;
    return last;
}    

3.2反转部分链表—递归92

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:

周练--C_第12张图片

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* successor = NULL; // 后驱节点
// 反转以 head 为起点的 n 个节点
struct ListNode*reverseN(struct ListNode* head, int n) {
    if (n == 1) 
    { 
        // 记录第 n + 1 个节点
        successor = head->next;
        return head;
    }
    // 以 head.next 为起点,需要反转前 n - 1 个节点
    struct ListNode* last = reverseN(head->next, n - 1);
    head->next->next = head;
    head->next = successor;
    return last;
}    
//
struct ListNode* reverseBetween(struct ListNode* head, int m, int n) 
{
    if (m == 1)
    {
        return reverseN(head, n);
    }
    head->next = reverseBetween(head->next, m - 1, n - 1);
    return head;
}/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* successor = NULL; // 后驱节点
// 反转以 head 为起点的 n 个节点
struct ListNode*reverseN(struct ListNode* head, int n) {
    if (n == 1) 
    { 
        // 记录第 n + 1 个节点
        successor = head->next;
        return head;
    }
    // 以 head.next 为起点,需要反转前 n - 1 个节点
    struct ListNode* last = reverseN(head->next, n - 1);
    head->next->next = head;
    head->next = successor;
    return last;
}    
//
struct ListNode* reverseBetween(struct ListNode* head, int m, int n) 
{
    if (m == 1)
    {
        return reverseN(head, n);
    }
    head->next = reverseBetween(head->next, m - 1, n - 1);//这步会最终回到m==1那里
    return head;
}

来源:力扣

 3.2.1-循环头插--反转部分链表92

周练--C_第13张图片

struct ListNode* reverseBetween(struct ListNode* head, int left, int right){
    struct ListNode*dummy=(struct ListNode*)malloc(sizeof(struct ListNode));//如果left等于一我们还得找头节点,所以此时我们创建一个头部哨兵节点,避免这种情况
    dummy->next=head;
    struct ListNode*pre=dummy;
    for(int i=0;inext;//先走到left节点之前的一个
    }
    struct ListNode*cur=pre->next;
    for(int i=0;inext;
        cur->next=next->next;
       
        next->next=pre->next;
        pre->next=next;//帮你试过了这三个顺序不能变,否则会超出时间限制
    }
    return dummy->next;//感觉好像还需要free;???


}

九.力扣876-链表的中间节点 (要求遍历一次)

1.题目 

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

2.图解 

 采用双指针-分两种情况

周练--C_第14张图片

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

十.链表中第K个结点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

tips:

fast先走k步,而后fast和slow共同走

考虑如果k大于数组长度,会造成空指针类型的报错,所以

代码

struct ListNode* getKthFromEnd(struct ListNode* head, int k)
{
    struct ListNode*fast,*slow;
    fast=slow=head;
    //while(--k)//进去k-1次
     while(k--)//进去k次
     {
        //k大于链表的长度
        if(fast==NULL)
        {
            return NULL;
        }
        fast=fast->next;
     }
    while(fast)
    {
       
        
        fast =fast->next;
        slow=slow->next;
    }
    return slow;
}

十一.分割链表

1.题目

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。
链接:https://leetcode.cn/problems/partition-list-lcci

2.图解周练--C_第15张图片

 3.代码

//注意头尾边界
struct ListNode* partition(struct ListNode* head, int x)
{
    struct ListNode*lesshead,*lesstail,*greaterhead,*greatertail;
   //开辟一个头结点方便后续尾插
   lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
   lesstail->next=NULL;
   greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
   greatertail->next=NULL;
   struct ListNode*cur=head;
   while(cur)
   {                                                 
       if(cur->valnext=cur;
           lesstail=cur;
       }
       else
       {
           greatertail->next=cur;
           greatertail=cur;
       }
        cur=cur->next;
        
   }
   lesstail->next=greaterhead->next; 
   struct ListNode*newNode=lesshead->next;
   greatertail->next=NULL; 
   free(lesshead);
   free(greaterhead);
   return newNode;
   
}

十二.回文链表

1.tips

1.双指针遍历

2.先找链表中部节点,后半部分链表翻转,判断前面链表与后面翻转后的链表中的元素(奇数或偶数个元素都没关系,也没必要去取消连接);

2.代码

//快慢指针找中值;翻转后半部分的链表;前部分后部分进行比较
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode*cur=head;
    struct ListNode*newNode=NULL;
    while(cur)
    {
        struct ListNode*next=cur->next;
        //头插
        cur->next=newNode;
        newNode=cur;
        //迭代
        cur=next;
    }
    return newNode;
}
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode*fast,*slow;
    fast=slow=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}
bool isPalindrome(struct ListNode* head)
{
    struct ListNode*mid=middleNode(head);
    struct ListNode*rhead=reverseList(mid);
    struct ListNode*curhead=head;
    struct ListNode*currhead=rhead;
    while(curhead&&currhead)
    {
        if(curhead->val!=currhead->val)
        {
            return false;
        }
        else//上一个相等了就继续去判断下个是否相等
        {
            
            curhead=curhead->next;
            currhead=currhead->next;
        }
    }
    return true;



}//快慢指针找中值;翻转后半部分的链表;前部分后部分进行比较
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode*cur=head;
    struct ListNode*newNode=NULL;
    while(cur)
    {
        struct ListNode*next=cur->next;
        //头插
        cur->next=newNode;
        newNode=cur;
        //迭代
        cur=next;
    }
    return newNode;
}
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode*fast,*slow;
    fast=slow=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}
bool isPalindrome(struct ListNode* head)
{
    struct ListNode*mid=middleNode(head);
    struct ListNode*rhead=reverseList(mid);
    struct ListNode*curhead=head;
    struct ListNode*currhead=rhead;
    while(curhead&&currhead)
    {
        if(curhead->val!=currhead->val)
        {
            return false;
        }
        else//上一个相等了就继续去判断下个是否相等
        {
            
            curhead=curhead->next;
            currhead=currhead->next;
        }
    }
    return true;



}

十三.160相交链表

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists 

时间复杂度O(n)

尾节点相同就是相交

找交点:长的链表先走(长度差步),然后俩同时走,第一个相同的就是所求。

十四.141.2环形链表

fast一次走两步,slow一次走一步,不带环fast会为空,带环,则两者相遇。

bool hasCycle(struct ListNode *head) {
    struct ListNode*fast=head,*slow=head;
    while(fast&&fast->next)//如果链表中的元素是奇数个并且不是环状
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            return true;
        }
    }
    return false;
    
    
}

1.双指针fast语slow,fast一次走两步,slow一次走一步,fast与slow一定在环内相遇,如果fast一次走的不是2步(n>2),则不一定。

2.环的入口如何找?

一个指针从相遇点开始走,另一个从链表头开始走,他们会在环的入口点相遇。

L=N*C-x,C指的是环的长度,x代表从入环点到相遇位置的距离,L表示从链表头到入环点之间的距离,N表示再相遇前,快指针在环内走的圈数。

L=(N-1)*C+C-x    (N-1)*C表示他从meetnode又走到meetnode

周练--C_第16张图片

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode*fast=head,*slow=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            //相遇
            struct ListNode*meet=slow;
            //公式证明的
            while(meet!=head)
            {
                meet=meet->next;
                head=head->next;
            }
            return meet;
        }

    }
    return NULL;
}

十五.快慢指针

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

周练--C_第17张图片

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

周练--C_第18张图片

struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    struct ListNode*newNode=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode*fast=head;
    struct ListNode*slow=newNode;
    newNode->next=head;
    for(int i=0;inext;
    }
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    struct ListNode*temp=slow->next;
    slow->next=temp->next;
    free(temp);
    slow=newNode->next;
    free(newNode);
    return slow;

}struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    struct ListNode*newNode=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode*fast=head;
    struct ListNode*slow=newNode;
    newNode->next=head;
    for(int i=0;inext;
    }
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    struct ListNode*temp=slow->next;
    slow->next=temp->next;
    free(temp);
    slow=newNode->next;
    free(newNode);
    return slow;

}

你可能感兴趣的:(每日一题,leetcode,算法,数据结构,c语言)