时间复杂度和空间复杂度课堂习题

例题1: 消失的数字

思路1:排序

1.把输入的数字从小到大排序

  • 先将n个数排序(冒泡,选择,插入)/使用C语言库函数qsort()排序,这样数字就和数组下标相对应

2.依次判断后一个数是否等于前一个数加1

  • 遍历数组nums,用nums[ i+1 ] - nums[ i ] 判断,等于1表示两个数相邻,等于2表示缺失了的那个数

  • 把对应的下标 i+1 输出即可

用于C语言的库qsort函数时间复杂度是O(nlogn),与题目不符合,所以这里就是提供思路,不提供实现代码


思路2:和差

用0到n的代数和减去a[0]到a[n-1]的和:(0+1+2+...+n)-(a[0]+a[1]+a[2]+...+a[n-1])

  • 直接先求0到n的累加和sum,然后遍历数组求和sumNums;

  • 用 sum - sumNums = 消失的数字

时间复杂度:O(n) 空间复杂度:O(1)

int missingNumber(int* nums, int numsSize)
{
        //先求 0 - n 的所有和
        //case 一:等差数列求和
        int sum = ( (0 + numsSize)* (numsSize+1)) >>1;
        int sumNums = 0; //保存nums数组的和
        //求数组的和
        for(int i = 0;i<numsSize;i++)
        {
            sumNums += nums[i];
        }
        //差值为消失的数字
        return sum - sumNums;
}
int miss_Number(int* nums, int n)
{
    int i;
    int sum = 0;
    int sumNums = 0;
    //case 二:将0到n相加
    for (i = 0;i <= numsSize;i++)
    {
        sum += i;
    }
    //将nums[0]到nums[0]相加
    for (i = 0;i < numsSize;i++)
    {
        sumNums += nums[i];
    }
    return sum - sumNums;
}

思路3:对应数字下标

数组中的值是几就在第几个位置写下这个值

  • 建立一个新数组,将n+1个数依次与数组中的数进行对应,剩下的即为缺失的。

时间复杂度:O(n) 空间复杂度:O(n)


思路4:

  • 给定一个值x=0(0与任何数异或等于任何数本身)

  • 令x与0到n的所有数进行异或

  • 令x与nums[0]到nums[n]每个值进行异或

  • 最后x就是那个缺失数

注:异或的特点就是相同的数,无论怎么排序,异或结果都一样(即满足交换律)

例:

1^2^1^2^3=3
1^3^2^2^1=3

时间复杂度:O(n) 空间复杂度:O(1)

代码实现:

int missingNumber(int* nums, int numsSize)
{
        //先异或0-numsSize的所有数字
        int x = 0;
        for(int i = 0 ;i<=numsSize;i++)
        {
                x ^= i;
        }
        //再异或nums数组所有的值
        for(int i = 0;i <numsSize;i++)
        {
            x ^= nums[i];
        }
        //最后的结果就是消失的数字
        return x;
}

例题2:旋转数组

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。

  • 你可以使用空间复杂度为 O(1) 的 1️⃣原地 算法解决这个问题吗?

1️⃣注:原地的意思是不开辟额外的空间

思路1:暴力求解,旋转k次

时间复杂度:O(N*k) 空间复杂度:O(1)

void rotate(int* nums, int numsSize, int k)
{
    for (int i = 0; i<k; i++)
    {
        int previous = nums[numsSize - 1];
        for (int j = 0; j<numsSize; j++)
        {
            int temp = nums[j];
            nums[j] = previous;
            previous = temp;
        }
    }

}

思路2:开辟数组(额外空间)法(本质:以空间换时间)

开辟一个新的动态数组,将尾端往前K个位置开始,将原数组中的元素拷贝到新数组中,在将前半部分拷贝过去,这样就完成了数组的旋转,最后只需将内容拷贝回原数组;

时间复杂度:O(N) 空间复杂度:O(N)


思路3:巧妙逆置

思路:将长度为numsize数组分为两部分,一部分为[0,numsize-k-1],另一部分为[k,numsize-1],分别进行转置,得到数组再全部进行转置即可巧妙得到题目要求的

图释:

时间复杂度:O(N) 空间复杂度:O(1)

//逆置
void Reverse(int *nums,int left,int right)
{
    while(left<right)
    {
        int temp=nums[left];
        nums[left]=nums[right];
        nums[right]=temp;
        left++;
        right--;
    }
}
void Rotate(int *nums,int numsize,int k)
{
    if(k>=numsize)
    {
        k%=numsize;
    }//k==numsize,相当于不旋转,结果即本身;k>numsize时,相当于旋转numsize-k
    Reverse(nums,0,numsize-k-1);//对前n-k个刷逆置
    Reverse(nums,numsize-k,numsize-1);//对后k个数逆置
    Reverse(nums,0,numsize-1);//整体逆置
}

总结:一道题有多种思路,我们不用都实现。只需要分析出每种思路的复杂度,选择复杂度最优的思路实现即可。这便是复杂度在实际中的意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值