初阶算法——删除有序数组中的重复项

这篇博客探讨了如何解决删除排序数组中重复元素的问题,提供了两种解法。解法一是传统的遍历加覆盖前移,虽然直观但效率较低,时间复杂度为O(n^2)。解法二是使用双指针法,通过一个头指针和一个尾指针,有效地在O(n)时间内完成任务,大大提高了效率。博客还配以直观的动图解释了双指针法的工作原理。

题目

给你一个升序排列的数组 nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

解法一

看到这种删除数组中元素的问题我们首先想到的是遍历数组,将要删除位置后面的元素前移覆盖。实现这种思想最直接的方法就是两重for循环嵌套,一层负责遍历,一层负责前移。

但是我们知道,一般算法的时间复杂度 >= O(n^{2})就不太好了,而该算法的时间复杂度就是O(n^{2}),那么是否有更优的算法呢? 

class Solution {
    public int removeDuplicates(int[] nums) {
        int len = nums.length;
        for(int i = 0;i < len - 1;i++){    //遍历数组
            if(nums[i] == nums[i+1]){
                for(int j = i+1;j < len - 1;j++){    //覆盖前移
                    nums[j] = nums[j+1];
                }
                len--;    //数组长度-1
                i--;    //下一个元素继续与本元素比较,用i--抵消i++
            }
        }
        return len;
    }
}

解法二——双指针法

这里我不做过多解释,看LeeCode给出的官方动图解就一目了然了

删除排序数组中的重复项 - 删除有序数组中的重复项 - 力扣(LeetCode)

 该方法处理只需要一个循环语句,所以复杂度仅仅为O(n),远小于我们的第一种方法!

class Solution {
    public int removeDuplicates(int[] nums) {
        int head = 1,tail = 0;
        if(nums.length < 2)
            return nums.length;
        while(head < nums.length){
            if(nums[head] != nums[tail]){
                tail++;
                nums[tail] = nums[head];
            }
            head++;
        }
        return tail+1;    //因为对于tail指针是先移动再赋值,所以长度等于末尾值+1
    }
}

C语言初阶的手把手教学内容包含多个方面,以下从学习心态和实践建议、简单游戏代码示例等方面进行阐述。 在学习心态和实践方面,编程学习者必须具备动手实践的素质,不能光听不做、眼高手低。例如在学习循环结构时,可能会出现走神后一知半解的情况,此时需要通过恶补和大量练习来掌握,动手实践才是真正学习编程的关键[^1]。 在代码实践方面,以猜数字游戏为例,代码实现如下: ```c #include<stdio.h> void menu() { printf("*********************************************************\n"); printf("******************** 1.play ***********************\n"); printf("******************** 0.exit ***********************\n"); printf("*********************************************************\n"); } int main() { int input; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: printf("猜数字\n"); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); return 0; } ``` 此代码实现了一个简单的猜数字游戏菜单,用户可以选择开始游戏或退出游戏。若选择错误,还能重新选择,直到选择退出游戏为止[^2]。 另外,在开发更复杂的游戏(如棋盘类游戏)前,可先创建头文件,定义所需的行数、列数、雷数目等信息,并声明一些必要的函数,如初始化棋盘、打印棋盘、布置雷、排查雷等函数。示例代码如下: ```c #include <time.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 9 //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //打印棋盘 void DisplayBoard(char board[ROW][COL], int row, int col); //布置雷 void SetMine(char mine[ROW][COL], int row, int col); //排查雷 void FindMine(char mine[ROW][COL], char show[ROW][COL], int row, int col); ``` 这为后续的代码实现奠定了基础[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值