《剑指offer》:数组部分

news2024/9/24 18:27:47

一、数组中重复的数字

题目描述:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

解题思路:考虑用哈希表进行操作。第一遍遍历整个数组更新哈希表值,第二遍遍历哈希表中对应元素值大于1的元素,输出该元素即为待查找元素。

classSolution {
public:
    // Parameters://        numbers:     an array of integers//        length:      the length of array numbers//        duplication: (Output) the duplicated number in the array number// Return value:       true if the input is valid, and there are some duplications in the array number//                     otherwise falseboolduplicate(int numbers[], int length, int* duplication){
        //判空操作if(numbers==NULL||length==0)
            returnfalse;
        //定义并遍历哈希表int hashpub[7]={0};
        for(int i=0;i<length;i++)
        {
            hashpub[numbers[i]]++;
        }
        for(int j=0;j<length;j++)
        {
            if(hashpub[numbers[j]]>1)
            {
                duplication[0]=numbers[j];
                returntrue;
            }
        }
        returnfalse;
    }
};

二、二维数组中的查找

题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路:本题可以从左下角或者右上角两个方向进行搜索,以从左下角搜索为例,若左下角的数字小于待查找数字,则应该继续往该行右侧查找;若左下角的数字大于待查找数字,则应该从上面的行查找。

#方法一:从左下角开始遍历
classSolution {
public:
    boolFind(int target, vector<vector<int> > array){
        //判空操作if(array.empty())
            returnfalse;
        int row=array.size()-1; //行遍历int column=0;
        while(row>=0&&column<array[0].size())
        {
            if(array[row][column]==target)
                returntrue;
            elseif(array[row][column]>target)
            {
                row--;
            }
            else
                column++;
        }
        returnfalse;
    }
};
#方法二:从右上角开始遍历
classSolution {
public:
    boolFind(int target, vector<vector<int> > array){
        if(array.empty())
            return0;
        int row=0;
        int col=array[0].size()-1;
        while(col>=0&&row<array.size())
        {
            if(target==array[row][col])
                return1;
            elseif(target<array[row][col])
                col--;
            else
                row++;
        }
        return0;
    }
};

三、构建乘积数组

题目:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2];)

解题思路:这里可以考虑B[j]=(A[0]*...*A[j-1])*(A[j+1]*...*A[n-1])两部分分别求解,先对右半部分乘,再对左半部分乘。

classSolution {
public:
    vector<int> multiply(const vector<int>& A){
        int n=A.size();
        vector<int>B(n);
        int ret=1;
        //求右边for(int i=0;i<n;ret*=A[i++])
        {
            B[i]=ret;
        }
        ret=1;
        for(int j=n-1;j>=0;ret*=A[j--])
        {
            B[j]*=ret;
        }
        return B;
    }
};

四、调整数组顺序使奇数位于偶数前面

题目描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路:本题要求不仅仅是奇数部分位于前半部分,还有偶数部分位于后半部分,要想保证相对位置不变,就不能单纯通过穷举遍历来进行交换,因此在这里我用冒泡排序来解决,时间复杂度是

编辑。

classSolution {
public:
    voidreOrderArray(vector<int> &array){
        //使用冒泡排序for(int i=0;i<array.size();i++)  //外层控制趟数for(int j=array.size()-1;j>i;j--)
                if(array[j]%2!=0&&array[j-1]%2==0)
                {
                    int tmp=array[j];
                    array[j]=array[j-1];
                    array[j-1]=tmp;
                }
    }
};

五、旋转数组的最小数字(考点:查找)

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路:本题可以考虑使用顺序查找的方法,时间复杂度为o(n)。同时,亦可以考虑使用二分查找的思路,由题目可以知后面部分总比前面部分小,那么只要将中间元素和左右两边进行对比,就可以知道最小值是在哪一边了,然后循环使用这种方法,最终只要right和left相邻,则right值即为最小值位置。

注意:在《剑指offer》中指出了两种我们需要考虑的情况

1、若排序数组的前面0个数字跑到最后面(即数组没变化),那么这时候第一个元素就是最小值,不用进行这么多遍历了

2、如果存在只挪动了1个元素的数组(如:{0,1,1,1}->{1,0,1,1}),这时候只能靠直接遍历了

classSolution {
public:
    intminNumberInRotateArray(vector<int> rotateArray){
        //判空操作if(rotateArray.empty())
            return0;
        int left=0,right=rotateArray.size()-1,mid=left;
        while(rotateArray[left]>=rotateArray[right])
        {
            mid=(left+right)/2;
            if(right-left==1)
            {
                mid=right;
                break;
            }
            if(rotateArray[mid]<=rotateArray[right])
                right=mid;
            elseif(rotateArray[mid]>=rotateArray[left])
                left=mid;
            if(rotateArray[left]==rotateArray[mid]&&rotateArray[left]==rotateArray[right])
                returnByOrder(rotateArray,left,right);
        }
        if(left>=right)
            return0;
        return rotateArray[mid];
    }
    intByOrder(vector<int>a,int l,int r){
        int min=a[l];
        for(int i=l;i<=r;i++)
            if(a[i]<min)
            {
                min=a[i];
            }
        return min;
    }
};

六、调整数组顺序使奇数位于偶数前面

题目描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路:两次遍历,第一次遍历只保存奇数的数,第二次保存在第一次的基础上,只是这次保留的是偶数。

classSolution {
public:
    voidreOrderArray(vector<int> &array){
        int size=array.size();
        if(size==0) return;
        vector<int>res;
        for(int i=0;i<size;i++)
        {
            if(array[i]%2!=0)
                res.push_back(array[i]);
        }
        for(int i=0;i<size;i++)
        {
            if(array[i]%2==0)
                res.push_back(array[i]);
        }
        array=res;
    }
};

反思:如果题目没有相对位置不变的要求?那么考虑使用快慢指针,头指针遍历前面的,尾指针遍历后面的,头指针遍历到偶数就和尾指针交换值,尾指针遍历到奇数就和头指针交换值。

classSolution {
public:
    voidreOrderArray(vector<int> &array){
        int size=array.size();
        if(size==0) return;
        int begin=0,end=size-1;
        while(begin<end)
        {
            while(begin<end&&array[begin]%2!=0)
                begin++;
            while(begin<end&&array[end]%2==0)
                end--;
            //交换if(begin<end)
            {
                int tmp=array[begin];
                array[begin]=array[end];
                array[end]=tmp;
            }
        }
    }
};

七、旋转矩阵

题目要求:实现矩阵顺时针旋转90度

解题思路:找到替换公式,本题迎刃而解。即

编辑

//方法一:时间复杂度和空间复杂度皆为O(n^2)classSolution {
public:
    voidrotate(vector<vector<int>>& matrix){
        int n = matrix.size();
        // C++ 这里的 = 拷贝是值拷贝,会得到一个新的数组auto matrix_new = matrix;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix_new[j][n - i - 1] = matrix[i][j];
            }
        }
        // 这里也是值拷贝
        matrix = matrix_new;
    }
};
//方法二:节省空间复杂度的办法classSolution {
public:
    voidrotate(vector<vector<int>>& matrix){
        int n = matrix.size();
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < (n + 1) / 2; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - j - 1][i];
                matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
                matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
                matrix[j][n - i - 1] = temp;
            }
        }
    }
};

反思:

水平翻转:

编辑

主对角线翻转:

八、数组中只出现一次的数字

题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解题思路:本题考查了异或操作。

  • 首先,从头到尾遍历,遍历出来不相同的数字

  • 然后通过位运算便利出来第二个出现了一次的数字

  • 最后找出来第一个出现了一次的数字

classSolution {
public:
    voidFindNumsAppearOnce(vector<int> data,int* num1,int *num2){
        int len=data.size();
        if(len<2) return;
        int counter=0;
        //先从头到尾遍历,遍历出来不同的数字for(int i=0;i<len;i++)
            counter=data[i]^counter;
        int flag=1;
        while(flag)
        {
            if(flag&counter)
                break;
            flag=flag<<1; //逐个遍历
        }
        for(int i=0;i<len;i++)
        {
            if((data[i]&flag)) *num1=*num1^data[i];
            else *num2=*num2^data[i];
        }
    }
};

九、数字在排序数组中出现的次数

题目描述:统计一数字在排序数组中出现的次数。

解题思路:使用两次遍历的方法,第一次使用二人查找,把相同的末位元素遍历出来;第二遍在数相同的有多少个。

classSolution {
public:
    intGetNumberOfK(vector<int> data ,int k){
        int start,end,mid,count=0,i;
        unsignedint len = data.size();
        if(!len)
            return0;
        start =0;
        end = len-1;
        mid = start;
        while(start<end)
        {
            mid = (start+end)/2;
            if(k >data[mid])
                start = mid+1;
            if(k<data[mid])
                end = mid-1;
            if(k==data[mid])
                break;
        }
        i=mid;
        while( (i>=0) && (k==data[i]))
        {
            i--;
            count++;
        }
        i=mid+1;
        while((i<len)&& (k==data[i]) )
        {
            i++;
            count++;
        }
        return count;
    }
};

十、剑指 Offer II 001. 整数除法(力扣

题目描述:给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 '*'、除号 '/' 以及求余符号 '%' 。

解题思路:使用减法代替除法。

classSolution {
    publicintdivide(int a, int b) {
        if(a==Integer.MIN_VALUE&&b==-1)
            return Integer.MAX_VALUE;
        int sign=((a>0)^(b>0))?-1:1;
        if(a>0)
            a=-a;
        if(b>0)
            b=-b;
        int res=0;
        while(a<=b){
            a-=b;
            res++;
        }
        return sign==1?res:-res;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/340679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言fscanf和fprintf函数的用法详解

fscanf() 和 fprintf() 函数与前面使用的 scanf() 和 printf() 功能相似&#xff0c;都是格式化读写函数&#xff0c;两者的区别在于 fscanf() 和 fprintf() 的读写对象不是键盘和显示器&#xff0c;而是磁盘文件。这两个函数的原型为&#xff1a;int fscanf ( FILE *fp, char …

【力扣-LeetCode】1138. 字母板上的路径-C++题解

1138. 字母板上的路径难度中等98收藏分享切换为英文接收动态反馈我们从一块字母板上的位置 (0, 0) 出发&#xff0c;该坐标对应的字符为 board[0][0]。在本题里&#xff0c;字母板为board ["abcde", "fghij", "klmno", "pqrst", &quo…

点云转3D网格【Python】

推荐&#xff1a;使用 NSDT场景设计器 快速搭建 3D场景。 在本文中&#xff0c;我将介绍我的 3D 表面重建过程&#xff0c;以便使用 Python 从点云快速创建网格。 你将能够导出、可视化结果并将结果集成到您最喜欢的 3D 软件中&#xff0c;而无需任何编码经验。 此外&#xff0…

总投资超500亿,广州白云机场三期扩建工程的IT投资更吸引人

【科技明说 | 每日看点】2023年基建大工程计划出台&#xff0c;广州白云机场三期将落实百亿元投资引发业内关注。据悉&#xff0c;广州白云机场三期扩建工程投资达537.7亿元&#xff0c;计划于2025年建成投产。这是中国民航历史上规模最大的改扩建工程&#xff0c;其扩建工程今…

TC3xx FlexRay™ 协议控制器 (E-Ray)-01

1 FlexRay™ 协议控制器 (E-Ray) E-Ray IP 模块根据为汽车应用开发的 FlexRay™ 协议规范 v2.1 执行通信【performs communication according to the FlexRay™ 1) protocol specification v2.1】。使用最大指定时钟&#xff0c;比特率可以编程为高达 10 Mbit/s 的值。连接到物…

Fluent Python 笔记 第 5 章 一等函数

在 Python 中&#xff0c;函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程 序实体: 在运行时创建能赋值给变量或数据结构中的元素 • 能作为参数传给函数能作为函数的返回结果 5.1 把函数视作对象 会用 map。 5.2 高阶函数 接受函数为参数&#xff0…

基于JAVA的超级玛丽设计与实现

技术&#xff1a;Java等摘要&#xff1a;随着计算机技术及网络技术的不断发展&#xff0c;电子游戏越来越普及。经典游戏“超级玛丽”因其本身所具有的娱乐性与教育意义而被人们广泛接受&#xff0c;在广大的青少年玩家中享有极高的知名度。Java语言作为一种完全面向对象的程序…

【AI 交互式聊天】无需等待 Bing ChatGPT : 已经有一个基于搜索结果响应的 AI 交互式聊天网站了!Perplexity

Perplexity: https://www.perplexity.ai 同样的问题&#xff0c;我问ChatGPT&#xff0c;回答如下&#xff1a; 实现财富自由需要一系列的步骤和策略&#xff0c;以下是一些建议&#xff1a; 1. 设定目标&#xff1a;明确你的财务目标&#xff0c;并制定一个有目标的计划来实…

吾爱破解2023安卓中级题

先来看看APP界面 拖到jadx&#xff0c;see see java 源码 关键是要让代码跳转到这里&#xff0c;我这里主要是修改smali&#xff0c;然后重新签名打包&#xff0c;当然&#xff0c;你也可以用frida或者objection hook 传参 模仿下面的两行代码&#xff0c;在位置1插入 :cond_6…

循环队列来了解一下!!

笔者在之前的一篇文章&#xff0c;详细的介绍了&#xff1a;队列之单向链表与双向链表的模拟实现&#xff1a;https://blog.csdn.net/weixin_64308540/article/details/128742090?spm1001.2014.3001.5502 感兴趣的各位老铁&#xff0c;可以参考一下啦&#xff01;下面进入循环…

基于Java+SpringBoot+Vue前后端分离酒店管理系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建、毕业项目实战、项目定制✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《S…

linux系统常用命令

目录 一、系统介绍 二、Linux常用命令 1、Linux命令格式 2、文件目录操作命令&#xff1a;ls 3、文件目录操作命令&#xff1a;cd 4、文件目录操作命令&#xff1a;cat 5、文件目录操作命令&#xff1a;more 6、文件目录操作命令&#xff1a;tail 7、创建文件命令&…

【三维点云】01-激光雷达原理与应用

文章目录内容概要1 激光雷达原理1.1 什么是激光雷达&#xff1f;1.2 激光雷达原理1.3 激光雷达分类三角法TOF法脉冲间隔测量法幅度调制的相位测量法相干法激光雷达用途2 激光雷达安装、标定与同步2.1 激光雷达安装方式考虑因素2.2 激光雷达点云用途2.3 数据融合多激光雷达数据融…

Rust学习入门--【7】Rust 数据类型

类型系统 对于任何一门语言都是重中之重&#xff0c;因为它体现了语言所支持的不同类型的值。 类型系统 也是 IT 初学者最难啃的三座大山之一&#xff0c;而类型系统之所以难以理解&#xff0c;主要是没有合适的现成的参考体系。 我们说类型系统 存在的目的&#xff0c;就是 …

【代码随想录】-动态规划专题

文章目录理论基础斐波拉契数列爬楼梯使用最小花费爬楼梯不同路径不同路径 II整数拆分不同的二叉搜索树背包问题——理论基础01背包二维dp数组01背包一维数组&#xff08;滚动数组&#xff09;装满背包分割等和子集最后一块石头的重量 II目标和一和零完全背包零钱兑换 II组合总和…

JVM学习03:垃圾回收

JVM学习03&#xff1a;垃圾回收 1、如何判断对象可以回收 1.1、引用计数法 记录当前对象被引用的次数&#xff0c;当引用次数为0时则进行垃圾回收。缺点&#xff1a;当两个对象互相引用但并没有其他对象再引用它们时&#xff0c;他们的引用次数都为1&#xff0c;无法对其进行…

Spring Security in Action 第三章 SpringSecurity管理用户

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;以实战为线索&#xff0c;逐步深入SpringSecurity相关知识相关知识&#xff0c;打造完整的SpringSecurity学习步骤&#xff0c;提升工程化编码能力和思维能力&#xff0c;写出高质量代码。希望大家都能够从中有所收获&#…

牛客寒假集训营6 E 阿宁的生成树

E-阿宁的生成树_2023牛客寒假算法基础集训营6 (nowcoder.com)开始慢慢补牛牛的题题意&#xff1a;最小生成树质数距离思路&#xff1a;最小生成树一共就两种算法&#xff0c;我们考虑Prim的过程初始连通块是1&#xff0c;然后考虑拿1和其他的结点连边当j-i<k时边权是gcd&…

【JavaSE】方法的使用

方法的使用BIT-5-方法的使用绪论1. 方法概念及使用1.1什么是方法1.2 方法定义1.3 实参和形参的关系&#xff08;重要&#xff09;1.4 没有返回值的方法2. 方法重载2.1 为什么需要方法重载2.2 方法重载概念3. 递归3.1 生活中的故事3.2 递归的概念3.2 递归执行过程分析3.3 递归练…

C++之异常处理

异常异常是面向对象语言处理错误的一种方式。当一个函数出现自己无法处理的错误时&#xff0c;可以抛出异常&#xff0c;然后输的直接或者间接调用者处理这个错误。语法捕获全部的异常try {//可能抛出异常的代码//throw异常对象 } catch(...) {//不管什么异常&#xff0c;都在这…