Leetcode刷题——数组题01

Leetcode刷题——数组题01

题目来源:Leetcode

题一:消失的数字

题目解读:一个长度为n数组中包含0-n所有整数,唯独缺失了一个,即很容易就知道完整的数组形式

代码(C语言版):

int missingNumber(int* nums, int numsSize){
    int res = 0;
    for(int i = 0;i<numsSize;i++){
        res ^= nums[i];
    }
    for(int i = 0;i<numsSize+1;i++){
        res ^= i;
    }
    return res;
}

异或运算知识点:a^a=0,0^a=a

代码解读:原数组与有缺失元素的数组逐一进行异或运算,两个数组中相同的异或后就为0,最后仅剩一个没有与它相同的数,即数组所缺失的元素。

题二:数组中数字出现的次数

题目解读:一个数组中只有一个两个数出现了一次,其余的数都出现了两次,求出这两个数,同时还有时间和空间复杂度的要求

代码(C语言版):

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* singleNumbers(int* nums, int numsSize, int* returnSize){
    int res = 0;
    for(int i = 0;i<numsSize;i++){
        res ^= nums[i];
    }
    //res = a ^ b
    //a != b,即a ^ b !=0,即res二进制中必有一位为1
    //找出最右1是哪一位
	int m = 0;
    while(m < 32){
        if(res&(1<<m)){
            break;
        }
        else{
            m++;
        }
    }
    //分离
    int x1 = 0, x2 = 0;
    for(int i = 0;i<numsSize;i++){
        if(nums[i]&(1<<m)){
            x1 ^=nums[i];
        }
        else{
            x2 ^=nums[i];
        }
    }
    int* retArr = (int*)malloc(sizeof(int)*2);
    retArr[0] = x1;
    retArr[1] = x2;
    *returnSize = 2;
    return retArr;
}

代码解读:这道题相当于上一道题的进阶版,我们同时可以用异或的方法进行思考,出现两次的数异或完都等于0,最终得到的结果为a^b(假设那两个数为a和b),下面只要求出a,b其中之一即可。异或运算法则是同为0异为1,因为a不等于b,所有a^b的结果中必定有一二进制位为1,则a和b的二进制位所在的那一位必然不同,即一个为0,另一个为1,我们可以通过这点不同将数组分为该二进制位为1的为一组,为0的为一组,这样,a和b就会被分进两个不同的组,将这两个组分别单独异或即可分别求出a和b.

难点在于如何获取到那个二进制为1的所在位

方法一

	int m = 0;
    while(m < 32){
        if(res&(1<<m)){
            break;
        }
        else{
            m++;
        }
    }

通过不断地将1左移每次都与异或地结果进行与运算,当结果为1则返回,得出m即左移的位数

方法二(附上完整代码)

int* singleNumbers(int* nums, int numsSize, int* returnSize){
    int res = 0;
    for(int i = 0;i<numsSize;i++){
        res ^= nums[i];
    }
    //res = a ^ b
    //a != b,即a ^ b !=0,即res二进制中必有一位为1
    int rightOne = res & (~res +1);//取出最右1
    //为1的那一位两个数必定一个为0,一个为1
    int onlyOne = 0;
    for(int i = 0;i<numsSize;i++){
        if((nums[i]&rightOne)==0){//找出所有那一位为1的数
            onlyOne ^= nums[i];//将这些数进行相与,最终得到a
        }
    }
    int* retArr = (int*)malloc(sizeof(int)*2);
    retArr[0] = onlyOne;
    retArr[1] = onlyOne ^ res;
    *returnSize = 2;
    return retArr;
}

主要就是int rightOne = res & (~res +1);//取出最右1这行代码进行操作,可直接取出最右1

题三:数组中数字出现的次数 II

题目解读:该题同样也为上一题的进阶版,只有一个出现一次,其余都出现三次,好在这题没有要求时间和空间复杂度,能力暂时还有限,思考了很久也没想出特别优的算法,最后通过先排序再寻找的方法解决了。

代码(C语言版):

int singleNumber(int* nums, int numsSize){

    //先排序
     for(int i = 0; i<numsSize-1;i++){
         int min = i;
        for(int j = i+1; j<numsSize;j++){
            if(nums[j]<nums[min]){
                min = j;
            }
        }
        int temp = nums[i];
        temp = nums[min];
        nums[min] = nums[i];
        nums[i] = temp;
    }
    int k = 0;
    for(k = 0;k<numsSize-2;){
        if(nums[k]==nums[k+1]&&nums[k]==nums[k+2]){
            k +=3;
        }
        else{
          break;
        }
    }
    return nums[k];
}

代码解读:首先排序,排序方法自选,这里我就随便选了个选择排序,排完序后,数组都有序,从头开始遍历,每次索引+3,当遇到连续的三个数不相等时便的得出出现一次的数。

题四:消失的两个数字

题目解读:该题同时也为上面题目的进进阶版,一数组包含1-n所有数,除了两个数不在,求出这两个数,同时还有时间和空间复杂度的要求。

这里提供了三种算法解答该题

方法一

显然也可以通过异或的方法求解

代码(C语言版):

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* missingTwo(int* nums, int numsSize, int* returnSize){
    int res = 0;
    for(int i = 1;i<=numsSize+2;i++){
        res ^= i;
    }
    for(int i = 0;i<numsSize;i++){
        res ^= nums[i];
    }
    //res = a ^ b
    //a != b,即a ^ b !=0,即res二进制中必有一位为1
    int rightOne = res & (~res +1);//取出最右1
    //为1的那一位两个数必定一个为0,一个为1
    int onlyOne = 0;
    for(int i = 0;i<numsSize;i++){
        if((nums[i]&rightOne)==0){//找出所有那一位为1的数
            onlyOne ^= nums[i];//将这些数进行相与
        }
    }
     for(int i = 1;i<=numsSize+2;i++){
        if((i&rightOne)==0){//找出所有那一位为1的数
            onlyOne ^= i;//将这些数进行相与
        }
    }
    int* retArr = (int*)malloc(sizeof(int)*2);
    retArr[0] = onlyOne;
    retArr[1] = onlyOne ^ res;
    *returnSize = 2;
    return retArr;
}

代码解读:得到a^b的结果和知道1所在的位之后,将缺失元素的数组和完整的数组中那一位为1的所有数进行异或便可得出a,再与a^b异或得到b.

方法二

因为该数组中的数是从1开始的而且近乎连续,我们可以就把他们分别当做数组的索引来看

代码(C语言版):

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* missingTwo(int* nums, int numsSize, int* returnSize){
    //生成数组空间
    int* arr =(int*)calloc(numsSize +3,sizeof(int));

	int* ans = malloc(sizeof(int) * 2), ansSize = 0;
	*returnSize = 2;
    //将对应的数放到对应的索引位置上
	for (int i = 0; i < numsSize; i++)
		arr[nums[i]]++;
	for (int i = 1; i < numsSize+3 ; i++)
	{
        //遍历数组将索引位置上为0的放到返回数组中去
		if (arr[i] == 0)
			ans[ansSize++] = i;
            if(ansSize==2)
			  break;
	}
	return ans;
}

代码解读:生成一个数组空间,遍历原数组,每取到一个数就把数组中与该数值相等的索引位置上的值+1(初始都为0),例如,第一个数为5,索引为5的位置上的值变为1,因为没有重复数,所以遍历完之后,数组上的值除了0就是1,为0的就是缺失的数,将其返回就好了。

方法三

利用求和以及两数平均数解答

代码(C语言版):

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* missingTwo(int* nums, int numsSize, int* returnSize){
    int res1 = 0;
    for(int i = 1;i<=numsSize+2;i++){
        res1 += i;
    }
    for(int i = 0;i<numsSize;i++){
        res1 -= nums[i];
    }
    //假设两个数分别为a和b,得到a+b
    int sumAvg = res1/2;
    int a = 0,b = 0;
     for(int i = 1;i<=numsSize+2;i++){
         if(i<=sumAvg){
             a += i;
         }
    }
     for(int i = 0;i<numsSize;i++){
         if(nums[i]<=sumAvg){
             a -= nums[i];
         }
    }
    int* retArr = (int*)malloc(sizeof(int)*2);
    retArr[0] = a;
    retArr[1] = res1-a;
    *returnSize = 2;
    return retArr;
}

代码解读:首先利用求和和作差得到a+b,求这两个数的平均数sumAvg,可以知道这两个数必然一个大于sumAvg,另一个小于sumAvg,于是我们便可以通过sumAvg将原数组分为两组,大于sumAvg的一组,小于sumAvg的一组,这样便可用同样的求和作差的方法分别求出a和b了。

补充

交换两个数的方法可以通过异或操作完成,但前提条件为这两个数必须不相等

int a=2,b=5;
//已知a!=b,交换两个数
a = a ^ b;
b = a ^ b;
b = a ^ b;
//交换完成
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老子裤子马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值