文章目录
- 1.二分查找
- 2.数组
- [1. 两数之和](https://leetcode.cn/problems/two-sum/)
- [26. 删除有序数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/)
- [27. 移除元素](https://leetcode.cn/problems/remove-element/)
- [35. 搜索插入位置](https://leetcode.cn/problems/search-insert-position/)
- [53. 最大子数组和](https://leetcode.cn/problems/maximum-subarray/)
- [66. 加一](https://leetcode.cn/problems/plus-one/)
- [118. 杨辉三角](https://leetcode.cn/problems/pascals-triangle/)
- [88. 合并两个有序数组](https://leetcode.cn/problems/merge-sorted-array/)
- [118. 杨辉三角](https://leetcode.cn/problems/pascals-triangle/)
- [119. 杨辉三角 II](https://leetcode.cn/problems/pascals-triangle-ii/)
- [121. 买卖股票的最佳时机](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/)
- [136. 只出现一次的数字](https://leetcode.cn/problems/single-number/)
- [169. 多数元素](https://leetcode.cn/problems/majority-element/)
- [219. 存在重复元素 II](https://leetcode.cn/problems/contains-duplicate-ii/)
- [228. 汇总区间](https://leetcode.cn/problems/summary-ranges/)
- [283. 移动零](https://leetcode.cn/problems/move-zeroes/)
1.二分查找
1.给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。题目链接
class Solution {
public int searchInsert(int[] nums, int target){
int l=0, r=nums.length-1;
int mid = (l+r)>>>1;
while(l<=r){
if(target>nums[mid]){
l = mid + 1;
} else if(target == nums[mid]){
return mid;
} else{
r = mid - 1;
}
mid = (l+r)>>>1;
}
return l;
}
}
2.给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。题目链接
class Solution {
public int mySqrt(int x) {
if (x == 0) {
return 0;
}
if (x < 4) {
return 1;
}
//返回的结果:mid^2 <= x
//返回结果的范围: 0-x
int left = 2;
int right = x / 2;
int mid = left + (right - left + 1) / 2;
while (left < right) {
if (mid > x / mid) {
right = mid - 1;
} else {
left = mid;
}
mid = left + (right - left + 1) / 2;
}
return left;
}
}
3.给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n]这个范围内没有出现在数组中的那个数。题目链接
class Solution {
public int missingNumber(int[] nums) {
//方法一 亦或
//1 xor 1 xor 2 xor 2跟1 xor 2 xor2 xor 1的结果没区别!
//只要是同一组数,顺序异或跟乱序异或的结果是一样的!
//任何数与0亦或都是它本身
int[] res = new int[nums.length * 2 + 1];
for (int i = 0; i < nums.length; i++) {
res[i] = nums[i];
}
for (int i = 0; i < nums.length + 1; i++) {
res[i + nums.length] = i;
}
int xor = 0;
for (int i = 0; i < res.length; i++) {
xor = xor ^ res[i];
}
return xor;
//方法二 先排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (i != nums[i]) {
return i;
}
}
return nums.length;
}
}
4.你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。题目链接
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1;
int right = n;
int mid = (left + right) >>> 1;
while(left < right){
if(isBadVersion(mid)){
right = mid;
} else {
left = mid + 1;
}
mid = (left + right) >>> 1;
}
return left;
}
}
5.给定两个数组 nums1 和 nums2 ,返回它们的交集。输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序 。题目链接
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//方法一
Set<Integer> set = new LinkedHashSet<>();
//1. 两个数组排好序
Arrays.sort(nums1);
Arrays.sort(nums2);
int i = 0;
int j = 0;
while (i < nums1.length && j < nums2.length) {
if (nums1[i] == nums2[j]) {
set.add(nums1[i]);
i++;
j++;
} else if (nums1[i] < nums2[j]) {
i++;
} else {
j++;
}
}
int[] rs = new int[set.size()];
int index = 0;
for (Integer s : set) {
rs[index++] = s;
}
return rs;
//方法二
List<Integer> arr = new ArrayList<>();
//1. 两个数组排好序
Arrays.sort(nums1);
Arrays.sort(nums2);
//2. 遍历第二个数组
for (int i = 0; i < nums2.length; i++) {
//2.1 对第一个数组进行二分查找
int left = 0;
int right = nums1.length - 1;
int mid = (left + right) >>> 1;
while (left <= right) {
if (nums2[i] < nums1[mid]) {
right = right - 1;
} else if (nums2[i] == nums1[mid]) {
arr.add(nums1[mid]);
break;
} else {
left = mid + 1;
}
mid = (left + right) >>> 1;
}
//2.2 判断当前元素值是否和下一个元素值相同,相同跳过下一个元素。
while (i < (nums2.length - 1) && nums2[i] == nums2[i + 1] ) {
i++;
}
}
int[] rs = new int[arr.size()];
int j = 0;
for (Integer a : arr) {
rs[j] = a;
j++;
}
return rs;
}
}
6.两个数组的交集 II 给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
List<Integer> arr = new ArrayList<>();
int i = 0;
int j = 0;
while(i<nums1.length && j<nums2.length){
if(nums1[i] == nums2[j]){
arr.add(nums2[j]);
i++;
j++;
}else if(nums1[i] < nums2[j]){
i++;
} else{
j++;
}
}
int[] rs = new int[arr.size()];
i = 0;
for(int a : arr){
rs[i++] = a;
}
return rs;
}
}
7.有效的完全平方数给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
进阶:不要使用任何内置的库函数,如sqrt 。
class Solution {
public boolean isPerfectSquare(int num) {
if(num == 0 || num == 1){
return true;
}
int left = 2;
int right = num/2;
int mid = (left + right) >>> 1;
while(left <= right){
if((long) mid * mid > num){
right = mid - 1;
} else if((long) mid * mid == num){
return true;
} else{
left = mid + 1;
}
mid = (left + right) >>> 1;
}
return false;
}
}
8.猜数字大小猜数字游戏的规则如下:
每轮游戏,我都会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。
如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。
你可以通过调用一个预先定义好的接口 int guess(int num) 来获取猜测结果,返回值一共有 3 种可能的情况(-1,1 或 0):
-1:我选出的数字比你猜的数字小 pick < num
1:我选出的数字比你猜的数字大 pick > num
0:我选出的数字和你猜的数字一样。恭喜!你猜对了!pick == num
返回我选出的数字。
public class Solution extends GuessGame {
public int guessNumber(int n) {
int left = 1;
int right = n;
int mid = (left + right) >>> 1;
while(left < right){
if(guess(mid) == -1){
right = mid - 1;
} else if(guess(mid) == 0){
return mid;
} else{
left = mid + 1;
}
mid = (left + right) >>> 1;
}
return mid;
}
}
9.排列硬币你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行可能是不完整的。给你一个数字 n ,计算并返回可形成 完整阶梯行的总行数。
class Solution {
public int arrangeCoins(int n) {
//方法一
if(n == 0) return 0;
if(n == 1 || n == 2) return 1;
long sum = 0;
int i = 1;
while(sum < n){
sum += i++;
}
if(sum == n){
return --i;
} else{
return i - 2;
}
//方法二
long left = 1;
long right = n;
long mid = (left + right) >>> 1;
long cur = 0;
while(left <= right){
cur = (mid+1)*mid/2;
if(cur == n){
return (int)mid;
} else if(cur > n){
right = mid - 1;
} else{
left = mid + 1;
}
mid = (left + right) >>> 1;
}
return (int)right;
}
}
10.二分查找给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int mid = (left + right) >>> 1; //int mid = left + (right - left)/2
while(left <= right){
if(nums[mid] > target){
right = mid - 1;
} else if(nums[mid] == target){
return mid;
} else{
left = left + 1;
}
mid = (left + right) >>> 1;
}
return -1;
}
}
2.数组
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
class Solution {
public int[] twoSum(int[] nums, int target) {
//方法一
int[] rs = new int[2];
for(int i = 0; i < nums.length - 1; i++){
for(int j = i + 1; j < nums.length; j++){
if(nums[i] + nums[j] == target){
rs[0] = i;
rs[1] = j;
break;
}
}
}
return rs;
//方法二
Map<Integer, Integer> hasMap = new HashMap<>(nums.length-1);
hasMap.put(nums[0],0);
for(int i=1; i<nums.length; i++){
if(hasMap.containsKey(target-nums[i])){
return new int[]{hasMap.get(target-nums[i]),i};
}
hasMap.put(nums[i],i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
26. 删除有序数组中的重复项
给你一个升序排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的相对顺序应该保持一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么nums的前 k 个元素应该保存最终结果。
将最终结果插入nums的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组并在使用 O(1) 额外空间的条件下完成。
class Solution {
public int removeDuplicates(int[] nums) {
//方法一
int i = 0;
int k = 1;
int j = 0;
while(i<nums.length - 1){
for(j = i + 1; j<nums.length;j++){
if(nums[i] == nums[j]){
continue;
}
i = j;
nums[k++] = nums[j];
break;
}
if(j >= nums.length){
i++;
}
}
return k--;
//方法二
int slow = 0;
for(int fast = 1; fast < nums.length; fast++){
if(nums[slow] != nums[fast]){
nums[++slow]=nums[fast];
}
}
return slow+1;
}
}
27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
class Solution {
public int removeElement(int[] nums, int val) {
int k = 0;
for(int i = 0; i<nums.length; i++){
if(nums[i] != val){
nums[k++] = nums[i];
}
}
return k;
}
}
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
class Solution {
public int searchInsert(int[] nums, int target){
int l=0, r=nums.length-1;
int mid = (l+r)>>>1;
while(l<=r){
if(target>nums[mid]){
l = mid + 1;
} else if(target == nums[mid]){
return mid;
} else{
r = mid - 1;
}
mid = (l+r)>>>1;
}
return l;
}
}
53. 最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
class Solution {
public int maxSubArray(int[] nums) {
long sum;
long maxnum = nums[0];
for(int i = 0; i < nums.length; i++){
sum = 0;
for(int j = i; j < nums.length; j++){
sum += nums[j];
maxnum = Math.max(sum,maxnum);
}
}
return (int)maxnum;
//动态规划
int tempMax = nums[0];
int max = nums[0];
for(int i = 1; i < nums.length; i++){
tempMax = Math.max(tempMax + nums[i], nums[i]);
max = Math.max(max,tempMax);
}
return max;
}
}
66. 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
class Solution {
public int[] plusOne(int[] digits) {
for(int i=digits.length-1;i>=0;i--){
if(digits[i]!=9){
digits[i]++;
break;
}else{
digits[i] = 0;
if(digits[0]==0){
digits = new int[digits.length+1];
digits[0] = 1;
break;
}
}
}
return digits;
}
}
118. 杨辉三角
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> out = new ArrayList<List<Integer>>();
for (int i = 0; i < numRows; i++) {
List<Integer> arr = new ArrayList<Integer>();
for (int j = 0; j < i + 1; j++) {
if (i == 0) { // 第一层
arr.add(1);
} else if (j == 0 || j == i){ // 第二层 + 下面层的最外面的两个数字
arr.add(1);
} else {
List<Integer> tmp = out.get(i - 1);
arr.add(tmp.get(j - 1) + tmp.get(j)); // 计算
}
}
out.add(arr);
}
return out;
}
}
88. 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你合并 nums2 到 nums1 中,使合并后的数组同样按非递减顺序排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
// int m2 = m;
// int k = 0;
// for(;m<nums1.length;m++){
// nums1[m] = nums2[k++];
// }
// if(m != m2){
// Arrays.sort(nums1);
// }
int lastIndex = nums1.length - 1;
int i = m - 1;
int j = n - 1;
while(i>= 0 && j>=0){
if(nums1[i] > nums2[j]) nums1[lastIndex--] = nums1[i--];
else nums1[lastIndex--] = nums2[j--];
}
while(j>=0){
nums1[lastIndex--] = nums2[j--];
}
}
}
118. 杨辉三角
给定一个非负整数 *numRows
,*生成「杨辉三角」的前 numRows
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> out = new ArrayList<List<Integer>>();
for (int i = 0; i < numRows; i++) {
List<Integer> arr = new ArrayList<Integer>();
for (int j = 0; j < i + 1; j++) {
if (i == 0) { // 第一层
arr.add(1);
} else if (j == 0 || j == i){ // 第二层 + 下面层的最外面的两个数字
arr.add(1);
} else {
List<Integer> tmp = out.get(i - 1);
arr.add(tmp.get(j - 1) + tmp.get(j)); // 计算
}
}
out.add(arr);
}
return out;
}
}
119. 杨辉三角 II
给定一个非负索引 rowIndex
,返回「杨辉三角」的第 rowIndex
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
class Solution {
public List<Integer> getRow(int rowIndex) {
List<List<Integer>> out = new ArrayList<List<Integer>>();
for (int i = 0; i <= rowIndex; i++) {
List<Integer> arr = new ArrayList<Integer>();
for (int j = 0; j < i + 1; j++) {
if (i == 0) { // 第一层
arr.add(1);
} else if (j == 0 || j == i){ // 第二层 + 下面层的最外面的两个数字
arr.add(1);
} else {
List<Integer> tmp = out.get(i - 1);
arr.add(tmp.get(j - 1) + tmp.get(j)); // 计算
}
}
out.add(arr);
}
return out.get(rowIndex);
}
}
121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
class Solution {
public int maxProfit(int[] prices) {
int minPrice = prices[0];
int max = 0;
for (int i = 1; i < prices.length; i++) {
max = Math.max(max, prices[i] - minPrice);
minPrice = Math.min(minPrice, prices[i]);
}
return max;
}
}
136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
class Solution {
public int singleNumber(int[] nums) {
for(int i = 1; i < nums.length; i++ ){
nums[0] = nums[i]^nums[0];
}
return nums[0];
}
}
169. 多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
class Solution {
public int majorityElement(int[] nums) {
//自己
Map<Integer,Integer> maps = new HashMap<>();
int num = 0;
int rs = 0;
for(int i = 0; i < nums.length; i++){
maps.put(nums[i],0);
}
for(int i = 0 ; i < nums.length; i++){
maps.put(nums[i], maps.get(nums[i])+1);
if(maps.get(nums[i]) > num){
num = maps.get(nums[i]);
rs = nums[i];
}
}
return rs;
//方法二
int rs = 0;
int count = 0;
for(int i = 0; i < nums.length; i++){
if(count == 0) rs = nums[i];
if(nums[i] == rs) count++;
else count--;
}
return rs;
}
}
219. 存在重复元素 II
给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。
class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
//方法一
for(int i = 0; i < nums.length; i++){
for(int j = i + 1; j <= i + k && j < nums.length; j++){
if(nums[i] == nums[j] && Math.abs(i-j)<=k){
return true;
}
}
}
return false;
//方法二
Map<Integer, Integer> maps = new HashMap<>();
for(int i = 0; i < nums.length; i++){
if(maps.containsKey(nums[i]) && (i - maps.get(nums[i]) <= k)) return true;
maps.put(nums[i],i);
}
return false;
}
}
228. 汇总区间
给定一个 无重复元素 的 有序 整数数组 nums 。
返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。
class Solution {
public List<String> summaryRanges(int[] nums) {
//方法一
List<String> list = new ArrayList<>();
String rs = "";
String start = "";
boolean flag = true;
if(nums.length == 1){
list.add("" + nums[0]);
return list;
}
for(int i = 0; i < nums.length - 1; i++){
if(nums[i] + 1 == nums[i+1]){
if(flag){
start = nums[i] + "";
flag = false;
if(i == nums.length - 2){
rs = start + "->" + nums[i+1];
list.add(rs);
return list;
}
}
if(i == nums.length - 2){
rs = start + "->" + nums[i+1];
list.add(rs);
return list;
}
continue;
}
if(flag){
rs = start + nums[i];
}else{
rs = start + "->" + nums[i];
}
list.add(rs);
flag = true;
rs = "";
start = "";
if(i == nums.length - 2){list.add("" + nums[i+1]);}
}
return list;
//方法二
List<String> res = new ArrayList<>();
// i 初始指向第 1 个区间的起始位置
int i = 0;
for (int j = 0; j < nums.length; j++) {
// j 向后遍历,直到不满足连续递增(即 nums[j] + 1 != nums[j + 1])
// 或者 j 达到数组边界,则当前连续递增区间 [i, j] 遍历完毕,将其写入结果列表。
if (j + 1 == nums.length || nums[j] + 1 != nums[j + 1]) {
// 将当前区间 [i, j] 写入结果列表
StringBuilder sb = new StringBuilder();
sb.append(nums[i]);
if (i != j) {
sb.append("->").append(nums[j]);
}
res.add(sb.toString());
// 将 i 指向更新为 j + 1,作为下一个区间的起始位置
i = j + 1;
}
}
return res;
}
}
283. 移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution {
public void moveZeroes(int[] nums) {
int index = 0;
//遍历整个数组
for(int i = 0; i < nums.length; i++){
//寻找不为0的元素
if(nums[i] != 0){
int temp = nums[index];
nums[index++] = nums[i];
nums[i] = temp;
}
}
}
}