刷题总结(详细)

背景

通过对经典编程题练习总结,培养解题思维,锻炼解题能力。

一、题目大纲

1 寻找一个单身狗/两个单身狗

题目描述:

一个单身狗
题目:一个数组中只有一个数字是出现一次,其他所有数字都出现了两次。编写一个函数找出这个只出现一次的数字。
Example
有数组的元素是:1,2,3,4,5,1,2,3,4
只有5出现1次,要找出5.


两个单身狗
题目:一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。编写一个函数找出这两个只出现一次的数字。
Example
有数组的元素是:1,2,3,4,5,6,1,2,3,4
只有5和6出现1次,要找出5和6.

思路如下:
在这里插入图片描述


寻找一个单身狗代码如下:

//寻找一个单身狗
int find_dog(int arr[], int num)
{
	int tmp = 0;
	for (int i = 0; i < num; i++)   //将数组中的每个数组进行异或结果赋给tmp
	{
		tmp = tmp ^ arr[i];
	}
	return tmp;
}

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4 };
	int num = sizeof(arr) / sizeof(arr[0]);
	//find_dog(arr, num);
	printf("%d\n", find_dog(arr, num));
	return 0;
}

寻找两个单身狗代码如下:

//寻找两个单身狗
void find_two_dog(int arr[], int num, int* dog1, int* dog2)
{
	//1、所有数字的异或
	int tmp = 0;
	int pos = 0;   //用于统计两个单身狗的第几位为1,通过为1的为对其分组

	for (int i = 0; i < num; i++)
	{
		tmp = tmp ^ arr[i];    //result为两个单身狗的异或
	}

	//2、计算tmp的第pos为1
	for (int i = 0; i < 32; i++)
	{		 
		if (((tmp>>i) & 1) == 1)   //判断5、6两单身狗异或结果tmp中第pos为1 
		{
			pos = i;
			break;
		}
		pos++;    
	}
	for (int i = 0; i < num; i++)
	{
		//计算数组中元素的第pos为1的异或
		//(就是按数组中元素第pos位为1的元素分成一组进行异或得到一个单身狗)
		if (((arr[i] >> pos) & 1) == 1)
		{
			*dog1 = *dog1 ^ arr[i];
		}

		//计算数组中元素的第pos为0的异或
		//(就是按数组中元素第pos位为0的元素分成一组进行异或得到一个单身狗)
		if (((arr[i] >> pos) & 1) == 0)
		{
			*dog2 = *dog2 ^ arr[i];
		}
	 }
	
}

int main()
{
	int arr[] = { 1, 2, 3, 4, 7, 6, 1, 2, 3, 4 };
	int num = sizeof(arr) / sizeof(arr[0]);
	int dog1 = 0;
	int dog2 = 0;
	find_two_dog(arr, num, &dog1, &dog2);
	printf("%d\n", dog1);
	printf("%d\n", dog2);

	return 0;
}

2 消失数字

题目描述:

题目:数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

Leetcode原题链接

思路代码如下:

思路1:先对0-n进行等差数列求和sum,然后用一个for循环遍历这个数组用sum减去数组中的每一个数字,最后求得的sum中就是我们所缺失的那一个数字 。
时间复杂度:O(n)

int missingNumber(int* nums, int numsSize)   //数组 数组的大小
{
    int sum = (1 + numsSize) * numsSize / 2;   //0-n等差数列求和

    for (int i = 0; i < numsSize; i++)    //sum减去数组中的每一个数
    {
        sum -= nums[i];
    }
    return sum;
}

思路2:用单身狗思想解题(成对的数据中找不是成对的数据),利用for循环产生1-n个数字,对每个数字进行异或得到一个结果,利用for循环对数组中的每一个数进行异或得到一个结果,最后对这两个结果进行异或
时间复杂度:O(n)

int missingNumber(int* nums, int numsSize)   //数组 数组的大小
{
    int x = 0;   //用于记录每次异或的结果
    for (int i = 0; i <= numsSize; i++)    //x与0-n每个数字进行异或 结果赋给x
    {
        x = x ^ i;
    }

    for (int i = 0; i < numsSize; i++)    //将0-n异或的结果x与数组中的每个数组进行异或,结果赋给x
    {
        x = x ^ nums[i];
    }

    return x;
}

3 数字添加逗号

题目描述:

题目:对于一个整数N(1<= N <=2,000,000,000)比如980354535,我们常常要一位一位数这几个数字是多少位,但是如果在这个数字每三位加一个逗号,它会变得更加的易于朗读,因此,这个数字加上逗号成了如下的模样:98,364,535请写一个程序帮她完成这件事情
example
输入:980354535
输出:98,364,535

思路如下:

思路:我们将这个整数N一个一个的拆开成一个数字,然后将这个数字转换成字符型放入一个字符数组中,
关于逗号的打印两种思路
1、我们在这个数组中将先放入一个逗号在放入三个数据,然后遍历这个数组即可
2、我们在这个数组中将先放入三个数据在放入一个逗号,然后遍历这个数组即可

在这里插入图片描述

思路1代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int N = 0;
	scanf("%d", &N);     //1234567
	char arr[14] = {0};

	int i = 0;     //用于统计放了几个数
	int k = 0;     //记录存了几个数字
	while (N)
	{	
		if (k != 0 && k % 3 == 0)  //k!=0 表示这个数组中第一个位子不需要放逗号
		{
			arr[i++] = ',';        //每存3个数字就存一个逗号
		}

		 arr[i++]= N % 10 + '0'; //将数字转换为字符类型的数据倒着放入字符数组arr中
		 N = N / 10;	           //7,6,5,4,3,2,1
		 
		//if (k != 0 && k % 3 == 0)  //k!=0 表示这个数组中第一个位子不需要放逗号
		//{
			//arr[i++] = ',';        //每存3个数字就存一个逗号
		//}

		 k++;
	}

	for (--i; i >= 0; i--)         //倒着遍历这个数组
	{
		printf("%c", arr[i]);		
	}
	return 0;
}

思路2代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main()
{
	int N = 0;
	scanf("%d", &N);     //123456789
	char arr[14] = { 0 };

	int i = 0;     //用于统计放了几个数
	int k = 0;     //记录存了几个数字
	while (N)
	{
		arr[i++] = N % 10 + '0'; //将数字转换为字符类型的数据倒着放入字符数组arr中
		N = N / 10;	           
		k++;

	   if (k % 3 == 0)  //k!=0 表示这个数组中第一个位子不需要放逗号
	   {
		   arr[i++] = ',';        //每存3个数字就存一个逗号
	   }		
	}
	//以上循环完成之后数组中[9 8 7 , 6 5 4 , 3 2 1 ,]

	i = i - 1;                   //最后的一个数组不要打印
	for (--i; i >= 0; i--)       //倒着遍历这个数组
	{
		printf("%c", arr[i]);
	}
	return 0;
}

4 轮转数组

题目描述:

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
Example:
输入: 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]
Leetcode原题链接

思路代码如下:

方法一:暴力轮转(time O(n^2)(最坏情况) Space O(1))
数组中的元素向右轮转k个位置,我们可以拆分成每次轮转一个位置,先将数组中最后的一个数字保存,数组之前的元素往后面移动一位,然后将最后的一位数字,放到数组的第一个位置(下标为0的位置)。具体代码如下:

void rotate(int num[], int n, int k)
{
	k = k % n;         //注意:每轮转n次为一个周期
	int tmp;
	for (int i = 0; i < k; i++)
	{
		tmp = num[n -1];           //最后一个数字保存到tmp
		//int end = n - 2;
		//while (end >= 0)
		//{
		//	num[end + 1] = num[end];
		//	end--;
		//}
		for (int j = n - 1; j > 0; j--)    //数组中最后一位数字之前的数字依次往后移动一位
		{
			num[j] = num[j-1];   //注意这是j-1,而不是j--,如果你是j--则j = j-1,两边都是num[j]你这样没有交换呀
		}
		num[0] = tmp;           //将tmp中的数字保存到数组第0个位置
	}
}

方法二:辅助空间法(time O(n) Space O(n))
构造一个辅助空间arr,我们将数组nums中后k个元素移动到辅助空间arr前k个位置
将数组nums后numsSize - k个元素移动到辅助空间arr的后numsSize - k个位置
典型的以空间换时间

//一个数组中有10个数,如果这个10作为下标,则表示数组中最后一个数的下一个位置
void rotate(int* nums, int numsSize, int k)
{
	int* arr = (int*)malloc(sizeof(int) * numsSize);
	assert(arr!=NULL);                        //arr不等于空成立才会执行下面的程序,否则的话就会在这断言
	
	//for (int i = 0; i < numsSize; i++)
	//{
	//	arr[(i + k) % numsSize] = nums[i];
	//}
	k = k % numsSize;
	memcpy(arr, nums + numsSize - k, sizeof(int)*k);      //将nums数组中的后k个数拷贝到数组arr中的前k个位置
	memcpy(arr + k, nums , sizeof(int) * (numsSize -k));  //将nums前n-k个数拷贝到arr数组中的后n-k个位置
	memcpy(nums, arr , sizeof(int) * k); 
	 
	for (int j = 0; j < numsSize; j++)
	{
		nums[j] = arr[j];
	}
	free(arr);
	arr = NULL;
}

方法三:三段逆置法(time O(n^2)(最坏情况) Space O(1))
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]

在这里插入图片描述

//要被反转的数组首地址,区间[left,right]反转
void reverse(int *nums,int left,int right)
{
    assert(nums);

    while(left<right)
    {
        //需要使用交换两数功能,你可以也把这个常用功能封装成函数,这里就不封装了
        int tmp =nums[left];
        nums[left]=nums[right];
        nums[right]=tmp;

        //区间左右两头数交换后,向中间靠拢
        left++;
        right--;
    }
}

void rotate(int* nums, int numsSize, int k)
{
    assert(nums);
    //k超过numsSize大小的部分没意义
    k%=numsSize;

    //我们自己写一个反转数组的函数,注意传递的区间参数
    reverse(nums,0,numsSize-k-1);          //前n-k个逆置
    reverse(nums,numsSize-k,numsSize-1);   //后k个逆置 
    reverse(nums,0,numsSize-1);            //整体逆置
	return;
}

5 移除元素

题目描述

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
Example:
输入: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],也会被视作正确答案。

Leetcode原题链接

思路如下

1、先定义两个指向数组nums中0位置处的指针src与des,
2、如果nums[src]与val相等则将nums[src++] = num[des++] ; 如果不相等src++;
3、返回des值

在这里插入图片描述

代码如下

int removeElement(int* nums, int numsSize, int val)
{
    int src = 0;
    int des = 0;
    while(src<numsSize)
    {
    //如果不等于val值,覆盖尾插到des位置,des++,src++
    //等于val,就src++
        if(nums[src] != val)
        {
            nums[des++] = nums[src++];
        }
        else
        {
            src++;
        }
    }

    return des;   //这里注意返回的不是des+1,因为每次给nums[des]赋值完之后,都对des进行了++操作
                  //所以des指向的是最后一个元素的下一个位置。也就是元素的个数
}

6 删除有序数组中的重复项

题目描述

题目:给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
Example:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

Leetcode原题链接

思路如下:

1、如果nums[src] == nums[dst],则src++
2、如果nums[src]!=nums[dst],则++dst,令nums[dst] = nums[src]
3、最后由于dst表示的最后一个数的下标位置,我们要返回新数组的个数我们还需要+1;
(我们这个题目要求是找不同,只有不同的时候我们将dst指向的值赋值给src)

在这里插入图片描述
代码如下

int removeDuplicates(int* nums, int numsSize)
{
    int dst = 0;  //定义指向起始位置的指针
    int src = 0;
    //遍历数组 如果dst指向的值与src指向的值相等就继续遍历
    //如果不相等,dst就先指向下一个位置,在对dst进行赋值。
    while(src< numsSize)  
    {
        if(nums[dst] == nums[src])
        {
            src++;
        }
        else
        {
            dst++;
            nums[dst] = nums[src];
        }
    }

    return dst+1;
}

7 合并两个有序数组

题目描述

题目:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
Example:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

Leetcode原题链接

思路如下

思路1(space O(n))
1、开辟一个新数组nums3,将nums1与nums2中的每一个数进行比较,然后将其中较小的值放入nums3中
2、如果nums1先遍历完,则将nums2中的剩余元素赋值给nums3,否则将nums1中剩余数据赋值给nums3
3、将nums3中的数据放入nums1中;打印nums1;

思路2(space O(1))
1、定义指针end1,end,end2分别指向nums1中的m-1,m+n-1以及nums2中的n-1的位置
2、遍历nums1[end1]和nums2[end2]之前的元素。并对指针end1与指针end2的数据进行比较如果nums1[endl]>=nums2[end2],则将较大的数据放入nums1[end–]中,较大数组对应的指针也减减。只要有一个先遍历完,则循环终止。
3、如果指针end2比end1先遍历完,则说明nums2中的数据全部合并到了nums1中;如果指针end1比end2先遍历完,则还需将nums2中的剩余元素赋值给nums1[end1]之前的元素.

在这里插入图片描述
思路2代码如下:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    int end1 = m-1;  //定义三个指针
    int end2 = n-1;
    int end = n+m-1;
    
    //遍历nums1与nums2,判断指针end1与end2指向数据的大小
    while(end1>=0&&end2>=0)   
    {
        if(nums1[end1]>=nums2[end2])   
        {
            nums1[end--] = nums1[end1--];  //
        }
        else
        {
            nums1[end--] = nums2[end2--];
        }
    }
    //nums2中的数据还有剩余,将其剩余数字放入nums1中
    while(end2>=0&&end1<0)
    {
        nums1[end--] = nums2[end2--];
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值