如[0,1,3,50,75],返回[2, 4->49, 51->74, 76->99]
package 面试算法;
import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.List;
public class FindMissnumbers {
private static List<String> findmissnumbers (int [] nums ,int lower,int upper){
// Step1. define return value(需要返回)
List<String> rst = new ArrayList<>();
//Step2. handle corner case
if (nums==null||nums.length==0){
rst.add(lower+"->"+upper);//无数字,就返回给定区间
return rst;
}
//Step3. Fill in business logic
//3.1返回lower之后缺少的,如果nums第一个是3 lower是0
addToList(rst,lower,nums[0]-1);
//3.2返回lower至upper之间的(数组间)
int prev=nums[0];
int i =1;
while(i<nums.length){
int cur=nums[i];
if (cur!=prev+1){
addToList(rst,prev+1,cur-1);
}
prev=cur;
i++;
}
//3.3返回upper之前的
addToList(rst,nums[nums.length-1]+1,upper);
return rst;
}
//将前面散布抽出一个方法
private static void addToList(List<String> rst,int start,int end){//两种case,if else
if(start==end){
rst.add(String.valueOf(start));//Convert integer to string
}else if(start<end){
rst.add(start+"->"+end);
}
}
public static void main(String[] args) {
int [] nums= {0,1,3,50,75};
int lower=0;
int upper=99;
List<String> rst=findmissnumbers(nums,lower,upper);
System.out.println(rst.toString());
}
}
Linear search(O(n)
) VS Binary Search(O(logN)
)
To do binary search, the array must be SORTED!
(一定二分查找)
本质:每次搜索范围变1/2
实际开发中,当进行到迭代n时,出现Bug,要求给定迭代,返回是good还是bug。
E.g 1 2 3 4 5 6 7 8 9 10
G G G B B B B B B B
package 面试算法;
public class FirstBadVersion {
private static int FIRST_BAD=5;
private static boolean isBadVersion(int version){
if(version>=FIRST_BAD){
return true;
}
return false;
}
private static int firstBadVersion(int n){
int low =1;
int high=n;
while(low<high){
int middle=low+(high-low)/2;//不写(low+high)/2,避免overflow
if (isBadVersion(middle)){
high=middle;
}else {
low=middle+1;
}
}
return low;
}
public static void main(String[] args) {
int rst =firstBadVersion(10);
System.out.println(rst);
}
}
use dummy node to simplify corner cases
oneway,use two pointers
LeetCode
题目要求 O(logN) 的时间复杂度
,基本可以断定本题是需要使用二分查找
class Solution {
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0) return -1;
int start = 0;
int end = nums.length - 1; //二分法
int mid;
while (start <= end) {
mid = start + (end - start) / 2;
if (nums[mid] == target) {
return mid;
}
//前半部分有序,注意此处用小于等于
if (nums[start] <= nums[mid]) {//因为本身就是升序数组
//target在前半部分
if (target >= nums[start] && target < nums[mid]) {
end = mid - 1;
} else {
start = mid + 1;
}
} else {
if (target <= nums[end] && target > nums[mid]) {
start = mid + 1;
} else {
end = mid - 1;
}
}
}
return -1;
}
}
思路:字符串去重,构造一个新的空字符串,遍历老字符串,如果新的字符串中不存在老的字符,添加到新的字符串中。
代码:
public static String deleteRepeatString(String str) {
String s = "";
for(int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if(s.indexOf(c) == -1) {
s+=c;
}
}
return s;
}
}
二叉搜索树是一棵二叉树,每个节点都有以下特性:
大于左子树上任意一个节点的值,
小于右子树上任意一个节点的值。
递归实现非常简单:
如果根节点
root == null
或者根节点的值等于搜索值val == root.val,
返回根节点。
如果val
,进入根节点的左子树查找 searchBST(root.left, val)。 如果 val > root.val
,进入根节点的右子树查searchBST(root.right, val)。
给定一个
排序数组
和一个目标值
,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
输入:[1,3,5,6], 5 输出: 2
输入: [1,3,5,6], 2 输出: 1
模板
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length - 1; // 注意
while(left <= right) {
int mid = (left + right) / 2; // 注意
if(nums[mid] == target) { // 注意
// 相关逻辑
} else if(nums[mid] < target) {
left = mid + 1; // 注意
} else {
right = mid - 1; // 注意
}
}
// 相关返回值
return 0;
}
}
代码
public int searchInsert(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while (left<=right){
int mid=(left+right)/2;
if(nums[mid]==target){
return mid;
}else if(nums[mid]<target){
left= mid+1;
}else if (nums[mid]>target){
right=mid-1;
}
}
return left;
}
public static String countAndSay1(int n) {
StringBuilder s = new StringBuilder();
int p1 = 0;
int cur = 1;
if ( n == 1 )
return "1";
String str = countAndSay(n - 1);
for ( cur = 1; cur < str.length(); cur++ ) {
if ( str.charAt(p1) != str.charAt(cur) ) {// 如果碰到当前字符与前面紧邻的字符不等则更新此次结果
int count = cur - p1;
s.append(count).append(str.charAt(p1));
p1 = cur;
}
}
// if ( p1 != cur ){// 防止最后一段数相等,如果不等说明p1到cur-1这段字符串是相等的
// int count = cur - p1;
// s.append(count).append(str.charAt(p1));
// }
s.append(cur-p1).append(str.charAt(p1));
return s.toString();
}
题解:LeetCode
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length==0) return 0;//边界值
int i=0; //慢指针
for(int j=1;j<nums.length;j++){
if(nums[i]!=nums[j]){
i++;
nums[i]=nums[j];.//往前走
}
}
return i+1;
}
}
题目: 给定一个大小为 n 的数组,找到其中的
多数元素
。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
保证有多数元素存在
方法一:排序
思路:既然数组中有出现次数> ⌊ n/2 ⌋的元素,那
排好序
之后的数组中,相同元素总是相邻的。
数组中间的元素总是“多数元素”,毕竟它长度> ⌊ n/2 ⌋
代码:
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[(nums.length)/2];
}
}
方法二:摩尔投票
算法:候选人(cand_num)
初始化为nums[0]
,票数count初始化为1
。 当遇到与cand_num相同的数,则票数 count + 1,否则票数 count - 1。 当票数count为0
时,更换候选人
,并将票数count重置为1。 遍历完数组后,cand_num即为最终答案
进一步解释:投票法是遇到
相同的则票数 + 1
,遇到不同的则票数 - 1。 且“多数元素”的个数> ⌊ n/2 ⌋,其余元素的个数总和<= ⌊ n/2
⌋。 因此“多数元素”的个数 - 其余元素的个数总和 的结果 肯定 >= 1。 这就相当于每个“多数元素”和其他元素
两两相互抵消,抵消到最后肯定还剩余至少1个“多数元素”
代码:
class Solution {
public int majorityElement(int[] nums) {
int cand=nums[0],count=1; //初始化一个候选
for(int i=1;i<nums.length;i++){ //从第二个判断
if(cand==nums[i]) { //如果等于,则数量加1
++count;
}else if(--count==0){ //不等,count-1,减到0则换人
cand=nums[i];
count=1;
}
}
return cand;
}
}
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
此题没保证一定存在
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
首先我们得明确,n/k的众数最多只有k-1个,为什么呢?假设有k个众数,n/k *
k=n,这k个元素都是众数,还要不同,怎么可能啊。那么对于这个题,超过n/3的数最多只能有3-1 = 2
个,我们可以先选出两个候选人A,B。 遍历数组,分三种情况:候选1:> n/3 候选2:> n/3 其他:< n/3
十二、搜索二维矩阵 II(240)
编写一个高效的算法来搜索
m x n 矩阵
matrix 中的一个目标值target
。
该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
思路:从右上角开始和target比较,大于target则去去掉列,小于target去掉行,直至相等
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length==0||matrix[0].length==0){//起始判断
return false;
}
int row=0;
int col=matrix[0].length-1; //右上角的元素
while(col>=0 &&matrix.length>row){
if(target<matrix[row][col]){ //比列小,去掉列
col--;
}else if(target>matrix[row][col]){//比列大,去掉行
row++;
}else
return true;
}
return false;
}
}
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1
的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数
字。
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
int repeat=-1;
for(int num: nums){
if(!set.add(num)){
repeat=num;
break;
}
}
return repeat;
}
}
代码:但是如果是 0 1 2 3
public class LianXi {
public static void main(String[] args) {
//创建一个长度为4的数组
int[] arr = new int[4];
Scanner input = new Scanner(System.in);
//循环将用户的输入的数据放入数组中
for (int i = 0; i < arr.length; i++) {
System.out.println("输入第" + (i + 1) + "个数");
arr[i] = input.nextInt();
}
//依次将数组中的所有数都遍历出来
for (int j = 0; j < arr.length; j++) {
for (int j2 = 0; j2 < arr.length; j2++) {
for (int k = 0; k < arr.length; k++) {
//判断遍历后的数是否有重复的数字,剔除所有重复的数字之后打印出来
if (arr[j] != arr[j2] && arr[j] != arr[k] && arr[j2] != arr[k]) {
System.out.print((arr[j] * 100 + arr[j2] * 10 + arr[k]) + " ");
}
}
}
System.out.println();
}
}
}
把一个数组最开始的若干个元素
搬到数组的末尾
,我们称之为数组的旋转。输入一个递增排序
的数组的一个旋转,输出旋转数组的最小元素。例如,数组[3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
常规解法:出现后面数字比前面数字小,返回,如没有,返回第一个
class Solution {
public int minArray(int[] numbers) {
for(int i=0; i<numbers.length-1;i++){
if(numbers[i]>numbers[i+1])
return numbers[i+1];
}
return numbers[0]; }
}
思路:关键找到右边最小的
排序数组的查找问题
首先考虑使用 二分法 解决
,其可将遍历法的 线性级别 时间复杂度降低至 对数级别 。
class Solution {
public int findMin(int[] nums) {
int left=0,right=nums.length-1; //左右指针
while(left<right){ //终止条件
int mid=(left+right)/2; //中间值
if(nums[mid]>nums[right]){ //中间>右大,那么目标值在右侧【mid+1,right】
left=mid+1;
}else if(nums[mid]<nums[right]){
right=mid; //最小值在左侧
}else right--; //保证重复时候
}
return nums[left];
}
}
例如3、打印1、2、3、…999
思路:9、99、999.其实就是打印[1-10^n-1]
class Solution {
public int[] printNumbers(int n) {
int end= (int)Math.pow(10,n)-1;//默认不超过int
int[]arr=new int[end];
for(int i= 0;i <end; i++) {
arr[i]=i+1;
}
return arr;
}
}
例如:【1.23.4】——[3,1,2,4]
思路:双指针碰撞[leetcode]
class Solution {
public int[] exchange(int[] nums) {
int i = 0, j = nums.length - 1, tmp;
while(i < j) {
while(i < j && (nums[i]%2==1))i++;
while(i < j && (nums[j]%2==0)) j--;
tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
return nums;
}
}
在未排序的数组中找到
第 k 个最大
的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
思路:leetcode
方法一;时间复杂度为 O(NlogN),空间复杂度为O(1)
class Solution {
public int findKthLargest(int[] nums, int k) {
int i =nums.length;
Arrays.sort(nums);
return nums[i-k]; }
}
方法二:基于快排
我写的没看到排序数组两个字
class Solution {
public int search(int[] nums, int target) {
int count = 0;
for(int i = 0; i < nums.length; i++ ){
if(nums[i]==target){
count++; }
}
return count;
}
}
二分法解决排序数组
class Solution {
public int search(int[] nums, int target) {
return helper(nums, target) - helper(nums, target - 1);
}
int helper(int[] nums, int tar) {
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] <= tar) i = m + 1;
else j = m - 1;
}
return i;
}
}
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
leetcode
class Solution {
public int maxSubArray(int[] nums) {
int ans = nums[0];
int sum = 0;
for(int num: nums) {
if(sum > 0) {
sum += num;
} else {
sum = num;
}
ans = Math.max(ans, sum);
}
return ans;
}
}
题目:[1,2,3,4] k=2 结果[1,2][2,1]
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
Arrays.sort(arr);
int [] nums=new int [k];
for(int i =0; i<k ;i++){
nums[i]= arr[i];
}
return nums;
}
}
二分法:
时间复杂度 O(logN): 二分法为对数级别复杂度。
空间复杂度 O(1): 几个变量使用常数大小的额外空间。
class Solution {
public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] == m) i = m + 1;
else j = m - 1;
}
return i;
}
}
遍历:
只要比较数组下标和该下标对应的值即可,再排除缺失0和缺失最后一项两个特殊情况。
class Solution {
public int missingNumber(int[] nums) {
if(nums[0]==1) return 0;
for(int i= 0;i<nums.length;i++){
if(nums[i]!=i) return i;
}
return nums.length;
}
}
如果数组中有重复数字,返回true
自己想的 时间空间O(n)
class Solution {
public boolean containsDuplicate(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for(int i=0;i<nums.length;i++){
set.add(nums[i]);
}
return !(nums.length==set.size());
}
}
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>(nums.length);
for (int x: nums) {
if (set.contains(x)) return true;
set.add(x);
}
return false;
}
leetcode
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
public class Solution219 {
public static boolean containsNearbyDuplicate(int [] nums, int k){
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i <nums.length ; i++) {
if(set.contains(nums[i])){ //包含一个,返回true
return true;
}
set.add(nums[i]); //没有,添加进去
if(set.size()>k){ //这个哈希表长度为k
set.remove(nums[i-k]);
}
}
return false;
}
Leetcode
在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。
解法一:
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
for (int i = 0; i < nums.length; ++i) {
for (int j = Math.max(i - k, 0); j < i; ++j) {
if (Math.abs(nums[i] - nums[j]) <= t) return true;
}
}
return false;
}
// Time limit exceeded.