数组总结:双指针,有序二分,分治;

1.找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

重点是:使用set集合,set中不能储存重复的值

题目链接:力扣

class Solution {public int findRepeatNumber(int[] nums) {//使用set集合,set中不能储存重复的值Set<Integer> set = new HashSet<Integer>();for(int num : nums){if(set.contains(num)) return num;set.add(num);}return -1;}
}

2.在有本身性质的二维数组中,查找值。

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

题目链接:力扣

根据其递增的性质,将正方形立起来,像是二叉树,选取左下角作为遍历的开始。元素向上,值小于该元素,元素向右,值大于该元素

class Solution {public boolean findNumberIn2DArray(int[][] matrix, int target) {//从数组的最后一行的第一个元素开始搜查找,元素所在的列以上的值小于该元素,元素右边行的值大于该元素//1.当遍历到元素时,该元素的值大于target时,行--;元素值小于target,列++int i = matrix.length-1;int j = 0;while(i>=0 && j<matrix[0].length){if(matrix[i][j]>target) i--;else if(matrix[i][j]<target) j++;else return true;}return false;}
}

3.旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。

题目链接:力扣

方法一:暴力查找

class Solution {public int minArray(int[] numbers) {int min = numbers[0];for(int i= 1; i<numbers.length;i++){if(numbers[i]<min){min = numbers[i];}}return min;}
}

方法二:二分法

class Solution {public int minArray(int[] numbers) {int i =0;int j = numbers.length-1;//二分法:此数组从开始到旋转点是递增,从旋转点到末尾也是递增,左递增数组,右递增数组,所以用二分法//选取中间值和区间的最右边的值比较,//跳出循环时i=jwhile(i<j){int m = (i+j)/2;if(numbers[m]>numbers[j]) i = m+1;else if(numbers[m] <numbers[j]) j =m;else j--;}return numbers[j];}
}

4.矩阵中的路径(DFS+剪枝)

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

题目链接:力扣

题目分析:本题是典型的矩阵搜索问题,可以使用深度优先搜索(DFS)+ 剪枝

  • 深度优先搜索:可以理解为暴力法遍历矩阵中所有字符串可能性。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
  • 剪枝: 在搜索中,遇到 这条路不可能和目标字符串匹配成功 的情况(例如:此矩阵元素和目标字符不同、此元素已被访问),则应立即返回,称之为 可行性剪枝 。

算法流程:

  • 递归参数: 当前元素在矩阵 board 中的行列索引 i 和 j ,当前目标字符在 word 中的索引 k 。
  • 终止条件:

返回 false: (1) 行或列索引越界 或 (2) 当前矩阵元素与目标字符不同 或 (3) 当前矩阵元素已访问过 ( (3) 可合并至 (2) ) 。
返回 true : k = len(word) - 1 ,即字符串 word 已全部匹配。

  • 递推工作:

标记当前矩阵元素: 将 board[i][j] 修改为 空字符 '' ,代表此元素已访问过,防止之后搜索时重复访问。(当进行上下左右同时搜索时,有可能之前的元素,已经被访问过,所以要把以访问的元素置空,当递归回溯时,再修改为原来的数组值)
搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 (代表只需找到一条可行路径就直接返回,不再做后续 DFS ),并记录结果至 res 。
还原当前矩阵元素: 将 board[i][j] 元素还原至初始值,即 word[k] 。

  • 返回值: 返回布尔量 res ,代表是否搜索到目标字符串。
class Solution {public boolean exist(char[][] board, String word) {char[] chars = word.toCharArray();//确定单词word的起始位置for(int i = 0;i<board.length;i++){for(int j=0;j<board[0].length;j++){if(dfs(board,i,j,0,chars)) return true;}}return false;}//k表示寻找第几个word中的字符public boolean dfs(char[][] board,int i,int j,int k,char[] chars){//递归回溯的条件if(i<0 || i == board.length || j<0 || j== board[0].length || board[i][j] != chars[k]) return false;if(k == chars.length-1) return true;//继续递归条件,说明此时board[i][j]=chars[k]//此时将board[i][j]做遍历标记board[i][j]='\0';//这表示空字符//继续向该元素的上下左右递归boolean res = dfs(board,i-1,j,k+1,chars) || dfs(board,i+1,j,k+1,chars) ||dfs(board,i,j-1,k+1,chars) || dfs(board,i,j+1,k+1,chars);board[i][j]=chars[k];return res;}
}

5.机器人的运动范围-(DFS+剪枝)

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

题目链接:力扣

此题与上题的区别:

  • 递归时指向右和下递归,不用再判断左上,每回溯一次加1;
  • 算i,j的和时,算法,圆圈加点表示求余

class Solution {//初始化矩阵int m,n;// int[][] arr;//定义--这个数组没用,因为已经知道长度了int k;boolean[][] value;//用于记录哪个格子被记录过public int movingCount(int m, int n, int k) {this.m = m;this.n = n;// arr = new int[m][n];//初始化this.k = k;value = new boolean[m][n];return dfs(0,0,0,0);}//深度优先遍历+剪枝public int dfs(int i,int j,int si,int sj){//递归回溯条件if(i ==m || j==n || si + sj > k || value[i][j]) return 0;//此时这个递归值为0;//设置ij点被遍历value[i][j] = true;return 1+dfs(i+1,j,(i+1) %10 ==0 ?si-8:si+1,sj)+dfs(i,j+1,si,(j+1) %10 ==0 ?sj-8:sj+1);}}

6.数组中次数超过一半的数

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

方法一:HashMap

方法二:排序

方法三:摩尔投票法

//方法一:哈希表
class Solution {int n;public int majorityElement(int[] nums) {Map<Integer,Integer> map = new HashMap<>();for(int num:nums){// if(map.get(num) == null) map.put(num,1);// else map.put(num,map.get(num)+1);map.put(num,map.getOrDefault(num,0)+1);if(map.get(num)>nums.length>>1) return num;}return 0;}
}//方法二:排序法
class Solution {int x=0;//用于记录最终的x出现次数超过一半的值public int majorityElement(int[] nums) {//投票法:不同就会为负值,题目中数超过数组的长度一半,最后剩下这个数字int vote =0;for(int num : nums){if(vote == 0) x = num;vote += x==num ? -1:1;}return x;}
}

7.最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

题目链接:力扣

方法一:用数组自带的排序Arrays.sort(arr);

方法二: 快排的改进

方法三:堆排序(这里也是用的自带的堆排序函数)

方法一:快排
class Solution {public int[] getLeastNumbers(int[] arr, int k) {//基于快速排序的,找到第k+1个最小值,那么从数组的最左边开始到k-1索引处//正好是确定值,基于快速排序改进,不用向左右连续递归if(k >= arr.length) return arr;quickSort(arr,k,0,arr.length-1);return Arrays.copyOf(arr,k);}public void quickSort(int[] arr,int k,int l,int r){int i = l;int j = r;//参考值是arr[l]while(i<j){//这里是等于,防止了死循环,这里好像必须先向再右边寻找while(i<j && arr[j]>=arr[l]){j--;}while(i<j && arr[i]<=arr[l]){i++;}swap(arr,i,j);}swap(arr,i,l);//这里防止栈溢出//如果划分值arr[l],l=k,此时划分完左边是最小的k个数if(k>i) quickSort(arr,k,i+1,r);if(k<i) quickSort(arr,k,l,i-1);return;//这里k=i时退出循环}public void swap(int[] arr,int i,int j){int tem = arr[i];arr[i] = arr[j];arr[j]= tem;}
}

8.把数组排成最小的数

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

输入: [3,30,34,5,9]
输出: "3033459"

题目链接:力扣

解析:

此题求拼接起来的最小数字,本质是一个排序问题。设数组nums中任意两数字的字符串为x和y,

则判断顺序为(最小数字)

  • 若x+y>y+x,则x"大于"y,要求最小数,此时y应该排在x前面
  • 反之,x"小于“y,此时应该排在x的y前面。

算法流程:

1.初始化:字符串列表strs,保存各数字的字符串格式

2.应用以上”排序判断规则“,对strs执行排序;

3.返回值:拼接strs中的所有字符串,并返回

class Solution {public String minNumber(int[] nums) {//1.将数组转为字符数组String[] strs = new String[nums.length];for(int i =0;i<nums.length;i++){strs[i]=String.valueOf(nums[i]);}//2.快排quickSort(strs,0,strs.length-1);//3.将字符数组转为字符串,此时借助stringbufferStringBuilder res = new StringBuilder();for(String num:strs){res.append(num);}return res.toString();}//根据本题特定的规则进行快排public void quickSort(String[] strs,int l,int r){//跳出递归的条件if(l>=r) return;int i = l;int j = r;String tem=strs[i];while(i<j){while(i<j && (strs[j]+strs[l]).compareTo(strs[l]+strs[j])>=0) j--;while(i<j && (strs[i]+strs[l]).compareTo(strs[l]+strs[i])<=0) i++;//交换两个值tem = strs[i];strs[i]=strs[j];strs[j]=tem;}//交换l,i值strs[i]=strs[l];strs[l]=tem;//向左右递归quickSort(strs,l,i-1);quickSort(strs,i+1,r);}
}

8.一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

题目链接:力扣

按位于 & ,这里要加括号

class Solution {public int[] singleNumbers(int[] nums) {//异或规律 nums中的值异或结果是:剩下两个不同的数字异或int res = 0;//最后异或结果int m = 1;//用于移位的数int x =0;int y=0;//遍历数组提取出异或值for(int num : nums){res = res^num;}//寻找从右开始寻找第一个res为1的位while((res & m) ==0){//这里只能是判断是不是0,m=m<<1;//1左移}for(int num : nums){if((num & m)==0)  x=x^num;else y=y^num;}return new int[]{x,y};}
}

9.在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

题目链接:力扣

方法一:map

class Solution {public int singleNumber(int[] nums) {//用哈希表Map<Integer,Integer> map = new HashMap<>();for(int num:nums){if(map.get(num) !=null){map.put(num,map.get(num)+1);}else{map.put(num,1);}}//遍历map数组for(Map.Entry<Integer,Integer> entry : map.entrySet()){if(entry.getValue() ==1) return entry.getKey();}return 0;}
}

方法二:根据3个数相同,统计二进制数对应位数 1的个数

class Solution {public int singleNumber(int[] nums) {//利用所有数字出现了3次为标准//根据题目要求 最长是31为int[] counts = new int[32];//统计所有数字每位 1 出现的次数,for(int i =0;i<nums.length;i++){//统计nums[i]在相应位置出现的次数for(int j =0;j<32;j++){counts[j] += nums[i] & 1;//是从低位开始的//无符号右移nums[i] >>>=1; }}int res =0;//对counts中的每个值对3取余for(int i =0;i<32;i++){res<<=1;//左移一位恢复值;res |= counts[31-i]%3;//高位倒着来,或运算是加入数组的1}return res;}
}

10.输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

题目链接:力扣

双指针遍历

class Solution {public int[] twoSum(int[] nums, int target) {int r = 0;int i=0;for(; i<nums.length;i++){if(nums[i]>=target){r = i;break;}}if(i==nums.length) r=nums.length-1;//int r = nums.length-1;int l =0;while(r>l){if(nums[l]+nums[r]==target){return new int[]{nums[l],nums[r]};}else if(nums[l]+nums[r]>target){r--;}else{l++;}}return null;}
}

11.分治思想:也算是动态规划,n*n矩阵,上下三角

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

题目链接:力扣

class Solution {public int[] constructArr(int[] a) {//体现了分治的思想if(a.length ==0) return new int[0];//先计算0到i-1的结果记为b[i],再乘i+1到n的连乘//b[i]结果是下三角int[] b = new int[a.length];b[0]=1;//计算下三角乘积for(int i=1;i<a.length;i++){b[i] = b[i-1] * a[i-1];}//下三角和上三角乘int tmp=1;for(int i=a.length-2;i>=0;i--){tmp *=a[i+1];//用一个值来统计b[i]=tmp *b[i];}return b;}
}

12.发现规律:最大值和最小值的差是小于5的

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

题目链接:力扣

class Solution {public boolean isStraight(int[] nums) {//本题大小王可以看成任意的数字,最大值和最小值不会超过5;//排序加遍历//统计0的数量int zero = 0;Arrays.sort(nums);for(int i =0;i<nums.length-1;i++){//这里i的最大值减1//判断会不会重复if(nums[i]==nums[i+1] && nums[i] !=0 ) return false;if(nums[i]==0) zero++;}return nums[4]-nums[zero]<5;}
}

剑指offer-数组\矩阵相关推荐

  1. 【算法】剑指 Offer 12. 矩阵中的路径

    文章目录 1.概述 2.我的算法 2.1 棋盘 2.1 开始节点 2.2 点没被访问 2.3 点是否在棋盘内 2.4 下一步 2.5 主方法 2.6 核心方法 2.7 测试类 3.leecode1 1 ...

  2. 8. 返回数组里出现次数最多的数字_剑指offer 数组中出现次数超过一半的数字

    题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. ...

  3. 【附可运行代码】剑指 Offer 12. 矩阵中的路径

    立志用最少的代码做最高效的表达 给定一个 m x n 二维字符网格 board 和一个字符串单词 word .如果 word 存在于网格中,返回 true :否则,返回 false . 单词必须按照字 ...

  4. 剑指 Offer 12. 矩阵中的路径

    这里使用了回溯算法,回溯算法是一种比较特别的DFS,它需要在达到搜索条件后,回溯上一次,继续搜索.普通的DFS适合找路径是否存在的问题,而回溯算法适合解决有几条路径的问题. 这里给出DFS的模板 df ...

  5. 剑指offer:矩阵中的路径(递归回溯法DFS类似迷宫)

    1. 题目描述 /*请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经 ...

  6. 《剑指offer》——矩阵中的路径

    更多2019年的技术文章,欢迎关注我的微信公众号:码不停蹄的小鼠松(微信号:busy_squirrel),也可扫下方二维码关注获取最新文章哦~ T: 题目描述 请设计一个函数,用来判断在一个矩阵中是否 ...

  7. 剑指offer:数组中重复的数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...

  8. [剑指offer] 数组中只出现一次的数字

    本文首发于我的个人博客:尾尾部落 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了偶数次.请写程序找出这两个只出现一次的数字. 解题思路 法一:大家都能想到的HashMap法 法二:异或法 ...

  9. 剑指offer之矩阵中的路径

    题目描述: 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左.右.上.下移动一格.如果一条路径经过了矩阵的某一格,那么 ...

  10. 剑指offer——12.矩阵中的路径(不熟)

    题目: 题1:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩 ...

最新文章

  1. python的 局部变量与全局变量
  2. c语言printf%%,c语言printf实现同一位置打印输出的实例分享
  3. PhpStorm代码换行设置
  4. shell后台执行命令-crontab
  5. php ajax.open的 url路径,php – 检测Ajax调用URL
  6. python中浅拷贝和深度拷贝的区别
  7. linux磁盘管理-vmware workstation模拟共享存储。
  8. http抓包实践--(二)--web网页抓包和fiddler修改包
  9. 算法笔记(胡凡)刷题笔记目录
  10. JDK7~13的新特性
  11. HikariCP数据库连接池详解
  12. php 公众号多图文消息,微信公众号怎么发布号多图文消息?微信公众号发布号多图文消息的方法...
  13. 中兴新支点操作系统_中兴新支点国产操作系统体验如何?笔者告诉你
  14. 芋道 Spring Cloud Netflix 注册中心 Eureka 入门
  15. linux查询电脑mac地址,查看MAC地址的几种方法汇总
  16. UML图详解(九)包图
  17. HOJ 10027 Longest Ordered Subsequence Extention
  18. 莫纳什大学计算机专业排名,2020年莫纳什大学排名前五的专业有哪些
  19. Android 基础知识系列之 Intent 常用方法
  20. 【c++的hash表和 java的hash表】

热门文章

  1. [代码]如何在选择画面中创建动态的select-options
  2. 中兴通讯首家完成TD-LTE 双载波外场测试下行速率高达260Mbps
  3. 【萌宠猫】第一次养猫咪
  4. ASP.NET验证控件之RangeValidator
  5. 吐血整理!某平台2980元OpenCV、图像识别等资料限时下载,仅此1天
  6. 蓝牙架构(10)—— 5 安全概述(5.1 安全架构 5.2 BR / EDR安全简单配对 5.3 仅安全连接模式 5.4 LE安全)
  7. 【内网安全】——CS操作指南(二)
  8. 与我相亲的剩女竟然是个小三
  9. 从微软网站下载Windows10官方ISO镜像
  10. 护士计算机考试内容,护士考试考哪些科目