1、数组
1.1 一维数组
1.1.1遍历
public class Arraytest {
public static void main(String[] args) {
int[] arr={1,3,4,5,6,8,9};
//顺序遍历
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
for (int a : arr) {
System.out.print(a);
}
//逆序遍历
for (int i = arr.length - 1; i >= 0; i--) {
System.out.print(arr[i]);
}
}
}
1.1.2 最值
//最大值:
public static int getMax(int[] arr) {
int max = arr[0];
for(int x=1; x<arr.length; x++) {
if(arr[x] > max) {
max = arr[x];
}
}
return max;
}
//最小值:
public static int getMin(int[] arr) {
int min = arr[0];
for (int x = 1; x < arr.length; x++) {
if (arr[x] < min) {
min = arr[x];
}
}
return min;
}
1.1.3 逆序
//方式1:
public static void reverse1(int[] arr) {
for(int x=0; x<arr.length/2; x++) {
int temp = arr[x];
arr[x] = arr[arr.length-1-x];
arr[arr.length-1-x] = temp;
}
}
//方式2:
public static void reverse2(int[] arr) {
for(int start=0,end=arr.length-1; start<=end; start++,end--) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
}
1.1.4 基本查找
// 方式1:
public static int getIndex1(int[] arr,int value) {
for(int i=0; i<arr.length; i++) {
if(arr[i] == value) {
return i;
}
}
return -1;
}
//方式2:
public static int getIndex3(int[] arr,int value) {
int index = -1;
for(int i=0; i<arr.length; i++) {
if(arr[i] == value) {
index = i;
break;
}
}
return index;
}
1.1.5 十大排序
1.1.5.1 冒泡排序
import java.util.Arrays;
public class BubbleSort {
public static int[] bubbleSort(int[] arr){
if (arr.length == 0) {
return arr;
}
int length = arr.length;
int temp;
//第多少趟排序
for (int i = 0; i < length-1; i++) {
//第多少行排序
for (int j = 0; j < length-i-1; j++) {
if (arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
return arr;
}
public static void main(String[] args) {
int[] arr={8,5,6,1,4,3,9,2};
int[] bubbleSort = BubbleSort.bubbleSort(arr);
System.out.println(Arrays.toString(bubbleSort));
}
}
1.1.5.2 选择排序
public class SelectionSort {
public static int[] selectionSort(int[] arr) {
if (arr.length == 0) {
return arr;
}
int length = arr.length;
int temp, minIndex;
for(int i=0; i<length-1; i++) {
minIndex = i;
for(int j=i; j<length; j++) {
if(arr[j] < arr[minIndex]) {
minIndex = j;
}
}
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
public static void main(String[] args) {
int[] arr = {10,15,25,37,21,13,9,10,15,2};
int[] sort = SelectionSort.selectionSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.3 插入排序
public class InsertionSort {
public static int[] insertionSort(int[] array) {
if (array.length == 0) {
return array;
}
int length = array.length;
int preIndex, current;
for (int i = 1; i < length; i++) {
preIndex = i - 1;
current = array[i];
while (preIndex >= 0 && array[preIndex] > current) {
array[preIndex + 1] = array[preIndex];
preIndex--;
}
array[preIndex + 1] = current;
}
return array;
}
public static void main(String[] args) {
int[] arr = {10, 15, 25, 37, 21, 13, 9, 10, 15, 2};
int[] sort = InsertionSort.insertionSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.4 希尔排序
public class ShellSort {
public static int[] shellSort(int[] array) {
if (array.length == 0) {
return array;
}
int len = array.length;
int temp, gap = len / 2;
while (gap > 0) {
for (int i = gap; i < len; i++) {
temp = array[i];
int preIndex = i - gap;
while (preIndex >= 0 && array[preIndex] > temp) {
array[preIndex + gap] = array[preIndex];
preIndex -= gap;
}
array[preIndex + gap] = temp;
}
gap /= 2;
}
return array;
}
public static void main(String[] args) {
int[] arr = {10,15,25,37,21,13,9,10,15,2};
int[] sort = ShellSort.shellSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.5 归并排序
public class MergeSort {
/**
* 两路归并算法,两个排好序的子序列合并为一个子序列
* @Title: merge
* @Description: 合并
* @param @param arr
* @param @param low
* @param @param mid
* @param @param high 参数
* @return void 返回类型
* @throws
*/
public static void merge(int[] arr, int low, int mid, int high) {
int[] temp = new int[high-low+1]; //新数组
int i = low; //左指针
int j = mid + 1; //右指针
int index = 0; //临时变量,用于计算数组下标
while(i<=mid && j<=high) { //把较小的数先移到新数组中
if(arr[i] < arr[j]) {
temp[index++] = arr[i++];
} else {
temp[index++] = arr[j++];
}
}
while(i <= mid) { //把左边剩余数移入数组
temp[index++] = arr[i++];
}
while(j <= high) { //把右边剩余数移入数组
temp[index++] = arr[j++];
}
for(int k=0; k<temp.length; k++) { //把新数组中的数覆盖原数组
arr[k+low] = temp[k];
}
}
/**
*
* @Title: mergeSort
* @Description: 合并排序
* @param @param arr
* @param @param low
* @param @param high 参数
* @return void 返回类型
* @throws
*/
public static int[] mergeSort(int[] arr, int low, int high) {
if (arr.length == 0) {
return arr;
}
int mid = ((high - low) >> 1) + low;
if(low < high) {
mergeSort(arr, low, mid); //左边
mergeSort(arr, mid+1, high); //右边
merge(arr, low, mid, high); //左右合并
}
return arr;
}
/**
*
* @Title: main
* @Description: 主函数——测试函数
* @param @param args 参数
* @return void 返回类型
* @throws
*/
public static void main(String[] args) {
int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
int[] sort = mergeSort(a, 0, a.length - 1);
for(int arr : sort) {
System.out.print(arr + " ");
}
}
}
第二种实现方法
public class Main {
public static void main(String[] args) {
int[] arr = {11,44,23,67,88,65,34,48,9,12};
int[] tmp = new int[arr.length]; //新建一个临时数组存放
mergeSort(arr,0,arr.length-1,tmp);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
public static void merge(int[] arr,int low,int mid,int high,int[] tmp){
int i = 0;
int j = low,k = mid+1; //左边序列和右边序列起始索引
while(j <= mid && k <= high){
if(arr[j] < arr[k]){
tmp[i++] = arr[j++];
}else{
tmp[i++] = arr[k++];
}
}
//若左边序列还有剩余,则将其全部拷贝进tmp[]中
while(j <= mid){
tmp[i++] = arr[j++];
}
while(k <= high){
tmp[i++] = arr[k++];
}
for(int t=0;t<i;t++){
arr[low+t] = tmp[t];
}
}
public static void mergeSort(int[] arr,int low,int high,int[] tmp){
if(low<high){
int mid = (low+high)/2;
mergeSort(arr,low,mid,tmp); //对左边序列进行归并排序
mergeSort(arr,mid+1,high,tmp); //对右边序列进行归并排序
merge(arr,low,mid,high,tmp); //合并两个有序序列
}
}
}
1.1.5.6 快速排序
public class QuickSortClass {
/**
* @param arr 待排序列
* @param leftIndex 待排序列起始位置
* @param rightIndex 待排序列结束位置
*/
private static void quickSort(int[] arr, int leftIndex, int rightIndex) {
if (leftIndex >= rightIndex) {
return;
}
int left = leftIndex;
int right = rightIndex;
//待排序的第一个元素作为基准值
int key = arr[left];
//从左右两边交替扫描,直到left = right
while (left < right) {
while (right > left && arr[right] >= key) {
//从右往左扫描,找到第一个比基准值小的元素
right--;
}
//找到这种元素将arr[right]放入arr[left]中
arr[left] = arr[right];
while (left < right && arr[left] <= key) {
//从左往右扫描,找到第一个比基准值大的元素
left++;
}
//找到这种元素将arr[left]放入arr[right]中
arr[right] = arr[left];
}
//基准值归位
arr[left] = key;
//对基准值左边的元素进行递归排序
quickSort(arr, leftIndex, left - 1);
//对基准值右边的元素进行递归排序。
quickSort(arr, right + 1, rightIndex);
}
public static void main(String[] args) {
int[] arr = {5, 1, 7, 3, 1, 6, 9, 4};
quickSort(arr, 0, arr.length - 1);
for (int i : arr) {
System.out.print(i + "\t");
}
}
}
第二种写法
public class QuickSort {
public static int[] quickSort(int[] array, int start, int end) {
if (array.length < 1 || start < 0 || end >= array.length || start >= end) {
return null;
}
//进行第一轮排序获取分割点
int index = partition(array, start, end);
//排序前半部分
quickSort(array, start, index-1);
//排序后半部分
quickSort(array, index+1, end);
return array;
}
/**
* 一次快速排序
* @param array
* @param lo
* @param hi
* @return key的下标index,也就是分片的间隔点
*/
public static int partition(int[] array, int lo, int hi) {
int key = array[lo];
while(lo<hi) {
//从后半部分向前扫描
while(array[hi]>=key && hi>lo) {
hi--;
}
array[lo] = array[hi];
//从前半部分向后扫描
while(array[lo]<=key && hi>lo) {
lo++;
}
array[hi] = array[lo];
}
array[hi] = key; //存入基准
return hi;
}
//测试
public static void main(String[] args) {
int[] arr = {1,9,3,12,7,8,3,4,65,22};
int[] sort = quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(sort));
}
}
第三种写法
import java.util.Arrays;
import java.util.Random;
/**
* 快速排序,使得整数数组 arr 有序
*/
public class QuickSortSwap {
public static int[] quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
quickSort(arr, 0, arr.length - 1);
return arr;
}
/**
* 快速排序,使得整数数组 arr 的 [L, R] 部分有序
*/
public static void quickSort(int[] arr, int L, int R) {
if (L < R) {
// 把数组中随机的一个元素与最后一个元素交换,这样以最后一个元素作为基准值实际上就是以数组中随机的一个元素作为基准值
swap(arr, new Random().nextInt(R - L + 1) + L, R);
int[] p = partition(arr, L, R);
quickSort(arr, L, p[0] - 1);
quickSort(arr, p[1] + 1, R);
}
}
/**
* 分区的过程,整数数组 arr 的[L, R]部分上,使得:
* 大于 arr[R] 的元素位于[L, R]部分的右边,但这部分数据不一定有序
* 小于 arr[R] 的元素位于[L, R]部分的左边,但这部分数据不一定有序
* 等于 arr[R] 的元素位于[L, R]部分的中间
* 返回等于部分的第一个元素的下标和最后一个下标组成的整数数组
*/
public static int[] partition(int[] arr, int L, int R) {
int basic = arr[R];
int less = L - 1;
int more = R + 1;
while (L < more) {
if (arr[L] < basic) {
swap(arr, ++less, L++);
} else if (arr[L] > basic) {
swap(arr, --more, L);
} else {
L++;
}
}
return new int[]{less + 1, more - 1};
}
/**
* 交换数组 arr 中下标为 i 和下标为 j 位置的元素
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] arr = {1,9,3,12,7,8,3,4,65,22};
int[] sort = QuickSortSwap.quickSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.7 堆排序
public class HeapSort {
public static int[] heapSort(int[] array) {
for(int i=array.length/2-1; i>=0; i--) { //构建一个大顶堆
adjustHeap(array, i, array.length-1);
}
for(int i=array.length-1; i>=0; i--) { //将堆顶记录和当前未经排序子序列的最后一个记录交换
int temp = array[0];
array[0] = array[i];
array[i] = temp;
adjustHeap(array, 0, i-1);
}
return array;
}
//构造大根堆
public static void adjustHeap(int[] array, int parent, int length) {
int temp = array[parent];
for(int child=2*parent; child<length; child*=2) {
// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
if(child<length && array[child]<array[child+1]) {
++child;
}
// 如果父结点的值已经大于孩子结点的值,则直接结束
if(temp >= array[child]) {
break;
}
array[parent] = array[child];
parent = child;
}
array[parent] = temp;
}
//测试
public static void main(String[] args) {
int[] arr = {1,9,3,12,7,8,3,4,65,22};
int[] sort = HeapSort.heapSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.8 计数排序
public class CountingSort {
public static int[] countingSort(int[] array) {
if (array.length == 0) {
return array;
}
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i=0; i<array.length; i++) {
if (array[i] < min) {
min = array[i];
continue;
}
if (array[i] > max) {
max = array[i];
}
}
int[] countArray = new int[max-min+1];
for (int i=0; i<array.length; i++) {
countArray[array[i]-min] ++;
}
int k = 0;
for (int i=0; i<countArray.length; i++) {
for (int j=0; j<countArray[i]; j++) {
array[k++] = min + i;
}
}
return array;
}
//测试
public static void main(String[] args) throws Exception {
int[] arr = {1,3,3,12,-7,8,8,4,65,-22, 8};
int[] sort = CountingSort.countingSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.9 桶排序
public class BucketSort {
public static int[] bucketSort(int[] arr){
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; i++){
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
//桶数
int bucketNum = (max - min) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for(int i = 0; i < bucketNum; i++){
bucketArr.add(new ArrayList<Integer>());
}
//将每个元素放入桶
for(int i = 0; i < arr.length; i++){
int num = (arr[i] - min) / (arr.length);
bucketArr.get(num).add(arr[i]);
}
// 对每个桶进行排序
for(int i = 0; i < bucketArr.size(); i++){
Collections.sort(bucketArr.get(i));
}
// 将桶中的元素赋值到原序列
int k = 0;
for(int i = 0; i < bucketArr.size(); i++){
// 对每个桶进行排序
// Collections.sort(bucketArr.get(i));
for(int j=0; j<bucketArr.get(i).size(); j++) {
arr[k++] = bucketArr.get(i).get(j);
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {10,15,25,37,21,13,9,10,15,2};
int[] sort = BucketSort.bucketSort(arr);
System.out.println(Arrays.toString(sort));
}
}
1.1.5.10 基数排序
public class RadixSort {
public static int[] radixSort(int[] arr, int d) { //d为桶数(10进制范围0~9即10个桶)
int max = arr[0];
for (int i=0; i<arr.length; i++) { //找出数组中的最大值
max = Math.max(max, arr[i]);
}
int key = 0; //数组中关键字的个数(以个位、十位、百位……为关键字,最大值的位数就是关键字的个数)
while (max > 0) {
max /= 10;
key ++;
}
List<ArrayList<Integer>> buckets = new ArrayList<>();
for (int i=0; i<d; i++) {
buckets.add(new ArrayList<>());
}
for (int i=0; i<key; i++) { //由个位开始,依次按关键字进行分配
for (int j=0; j<arr.length; j++) { //扫描所有元素,将其分配到桶
int index = arr[j] % (int)Math.pow(10, i+1) / (int)Math.pow(10, i); //取出个位、十位、百位……的数字
buckets.get(index).add(arr[j]);
}
//分配完成,将桶中元素复制回原数组
int counter = 0;
for (int j=0; j<10; j++) {
ArrayList<Integer> bucket = buckets.get(j); //关键字为j的桶
while (bucket.size() > 0) {
arr[counter++] = bucket.remove(0);
}
}
}
return arr;
}
//测试
public static void main(String[] args) {
int[] arr = {1,9,3,12,7,8,3,4,65,22};
int[] sort = RadixSort.radixSort(arr, 10);
System.out.println(Arrays.toString(sort));
}
}
1.2 二维数组
1.2.1 遍历
//第一种形式
public static void traversal(int[][] array){
//遍历行
for(int i=0;i<array.length;i++)
{
//遍历列
for(int j=0;j<array[i].length;j++)//利用嵌套for循环来遍历二维数组
{
System.out.println(array[i][j]);
}
}
}
//第二种形式
public static void traversal2(int[][] array){
for (int[] arr : array) {
for (int a : arr) {
System.out.println(a);
}
}
}
1.2.2 求和
public static int add(int[][] arr)//求和子函数
{
int sum=0;
for(int i=0;i<arr.length;i++)
{
for(int j=0;j<arr[i].length;j++)//利用嵌套for循环来遍历二维数组
{
sum+=arr[i][j];//每遍历出一个元素则叠加一次
}
}
return sum;//返回二维数组中个元素的和
}
1.2.3 均值
public static void average(int[][] arr)//求平均值
{
for(int i=0;i<arr.length;i++)
{
int sum=0;
for(int j=0;j<arr[i].length;j++)
{
sum=sum+arr[i][j];
}
int average=sum/3;
System.out.println(average);
}
}
1.2.4 最值
//获取最大值
public static void getMax(int[][] arr)
{
for(int i=0;i<arr.length;i++)
{
int max=arr[i][0];
for(int j=1;j<arr[i].length;j++)
{
if(arr[i][j]>max)
{
max=arr[i][j];
}
}
System.out.println(max);
}
}
//获取最小值
public static void getMin(int[][] arr)
{
for(int i=0;i<arr.length;i++)
{
int min=arr[i][0];
for(int j=1;j<arr[i].length;j++)
{
if(arr[i][j]<min)
{
min=arr[i][j];
}
}
System.out.println(min);
}
}
1.2.5查找
//暴力查找
public boolean Find(int target, int [][] array) {
if(array.length == 0)// 如果二维数组为空,直接返回false
{
return false;
}
for(int i=0;i<array.length;i++) {
for(int j=0;j<array[i].length;j++){
if(array[i][j] == target ) {
return true;
}
}
}
return false;
}
}
// 第二种 时间复杂度 O(M+N) 空间复杂度O(1)
public static boolean Find2(int target, int[][] array) {
if(array.length == 0)// 如果二维数组为空,直接返回false
{
return false;
}
int row=0; //第0行
int col=array[0].length-1; //第1行长度减1就是最后1列
int rowNumber=array.length;//总行数
while (row<rowNumber&&col>=0){
if (array[row][col]>target){
col--;
}
else if (array[row][col]<target){
row++;
}
else {
return true;
}
}
return false;
}
1.2.6 对角线遍历
//对角线遍历
public static int[] findDiagonalOrder(int[][] matrix) {
int[] a=new int[0];
if (matrix.length==0) {
return a;
}
int m = matrix.length;
int n = matrix[0].length;
int j=0;
int[] nums =new int[m*n];
boolean bXFlag = true;
for (int i = 0; i < m + n; i++) // 1. 坐标(x, y)相加的和是递增的
{
// 2. 逻辑处理是一样的,x,y的上限值是相反的
int pm = bXFlag ? m : n;
int pn = bXFlag ? n : m;
int x = (i < pm) ? i : pm - 1; // 3. 例如这一趟是 x 从大到小, x 尽量取最大
int y = i - x; // 3. 当初始值超过 x 的上限时,不足的部分加到 y 上面
while (x >= 0 && y < pn) // 4. 这一趟结束的判断是, x 减到 0 或者 y 加到上限
{
// 5. 方向相反时,x,y是相反的
nums[j]=bXFlag ? matrix[x][y] : matrix[y][x];
j++;
x--; // 2. 每一趟都是 x 或 y 其中一个从大到小(每次-1)
y++; // 2. 另一个从小到大(每次+1)
}
// 5. 循环进行
bXFlag = !bXFlag;
}
return nums;
}
1.2.7 旋转90度
public void rotate(int[][] matrix) {
int N = matrix.length;
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-1-j][i];
matrix[N-1-j][i] = matrix[N-1-i][N-1-j];
matrix[N-1-i][N-1-j] = matrix[j][N-1-i];
matrix[j][N-1-i] = temp;
}
}
}
//N*N型矩阵旋转90°相当于矩阵上下对折再沿对角线对折
public void rotate(int[][] matrix) {
int size = matrix.length;
for (int row = 0; row < size / 2; row++) {
for (int col = 0; col < size; col++) {
int temp = matrix[row][col];
matrix[row][col] = matrix[size - row - 1][col];
matrix[size - row - 1][col] = temp;
}
}
for(int row = 0 ; row < size ; row++){
for(int col = 0 ; col < row+1 ; col ++){
int temp = matrix[row][col];
matrix[row][col] = matrix[col][row];
matrix[col][row] = temp;
}
}
}
1.2.8 螺旋打印(顺时针或者逆时针)
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> list = new ArrayList<>();
//这里记住不要先设置变量,然后通过变量来判断长度是否为0,
//因为如果为空的时候,是得不到宽度的,会报错
if (matrix.length == 0 || matrix == null || matrix[0].length == 0) {
return list;
}
int len = matrix.length;
int widlen = matrix[0].length;
for (int i = 0; i < (Math.min(len, widlen) + 1) / 2; i++) {
for (int j = i; j < widlen - i; j++) {
list.add(matrix[i][j]);
}
for (int j = i + 1; j < len - i; j++) {
list.add(matrix[j][widlen - i - 1]);
}
for (int j = widlen - i - 2; j >= i; j--) {
//防止出现重复遍历的情况
if (list.size() == len * widlen) {
break;
}
list.add(matrix[len - i - 1][j]);
}
for (int j = len - i - 2; j > i; j--) {
//防止出现重复遍历的情况
if (list.size() == len * widlen) {
break;
}
list.add(matrix[j][i]);
}
}
return list;
}
1.3 经典题目源码
转置矩阵
class Solution {
public int[][] transpose(int[][] A) {
int R = A.length, C = A[0].length;
int[][] ans = new int[C][R];
for (int r = 0; r < R; ++r)
for (int c = 0; c < C; ++c) {
ans[c][r] = A[r][c];
}
return ans;
}
}
寻找两个正序数组的中位数
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length, length2 = nums2.length;
int totalLength = length1 + length2;
if (totalLength % 2 == 1) {
int midIndex = totalLength / 2;
double median = getKthElement(nums1, nums2, midIndex + 1);
return median;
} else {
int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
return median;
}
}
public int getKthElement(int[] nums1, int[] nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int length1 = nums1.length, length2 = nums2.length;
int index1 = 0, index2 = 0;
int kthElement = 0;
while (true) {
// 边界情况
if (index1 == length1) {
return nums2[index2 + k - 1];
}
if (index2 == length2) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return Math.min(nums1[index1], nums2[index2]);
}
// 正常情况
int half = k / 2;
int newIndex1 = Math.min(index1 + half, length1) - 1;
int newIndex2 = Math.min(index2 + half, length2) - 1;
int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= (newIndex1 - index1 + 1);
index1 = newIndex1 + 1;
} else {
k -= (newIndex2 - index2 + 1);
index2 = newIndex2 + 1;
}
}
}
}
直方图的水量
方法一:双指针
class Solution {
public int trap(int[] height) {
if(height.length < 3) return 0;
int left = 0, right = height.length - 1;
int leftmax = height[left], rightmax = height[right];
int res = 0;
while(left < right){
if(leftmax < rightmax){
res += leftmax - height[left++];
leftmax = Math.max(height[left], leftmax);
}else{
res += rightmax - height[right--];
rightmax = Math.max(height[right], rightmax);
}
}
return res;
}
}
方法二:
class Solution {
private int[] height;
public int trap(int[] height) {
if(height.length <= 2)
return 0;
this.height = height;
int sum = 0;
int start = 0, end = 0;
for(int i = 0; i < height.length; ++i) { //初次遍历,找到最高的柱子给到start,end
if(height[start] < height[i]) {
start = i;
end = i;
}
}
while(start > 0 || end < height.length-1){
//除start到end之外,找到最高柱子的位置 p,h[p] <=h[start] 并且h[p]<=h[end]
int p = findHighest(start,end);
if(start > p) { //p在start前面时,计算此时p到start之间的积水
for(int i = h+1; i < start; ++i) {
sum += height[p]-height[i];
}
start = p;
}
if(end < p) { //p在end后面时,计算此时end到p之间的积水
for(int i = end+1; i < p; ++i) {
sum += height[p]-height[i];
}
end = p;
}
}
return sum;
}
//这个方法的作用是找到 start到end之外(不包含自身)的最高的h[i],返回i
private int findHighest(int start, int end){
int p = -1, max = -1;
for(int i = 0; i < start; ++i) {
if(max < height[i]) {
max = height[i];
p = i;
}
}
for(int i = end+1; i < height.length; ++i) {
if(max < height[i]) {
max = height[i];
p = i;
}
}
return p;
}
}
绝对差不超过限制的最长连续子数组
class Solution {
public int longestSubarray(int[] nums, int limit) {
// 两个单调队列分别维护从left到right之间的最大值最小值
Deque<Integer> minDeque = new LinkedList<>();
Deque<Integer> maxDeque = new LinkedList<>();
int result = 0;
int left = 0, right = 0;
minDeque.offer(0);
maxDeque.offer(0);
while (right < nums.length) {
int minIdx = minDeque.peekFirst();
int maxIdx = maxDeque.peekFirst();
if (nums[maxIdx] - nums[minIdx] <= limit) {
// 最大值与最小值之差小于等于限制的情况下右指针右移
result = Math.max(result, right - left + 1);
++right;
// 维护两个单调队列
while (right < nums.length && minDeque.size() > 0 && nums[right] < nums[minDeque.peekLast()]) {
minDeque.pollLast();
}
minDeque.offerLast(right);
while (right < nums.length && maxDeque.size() > 0 && nums[right] > nums[maxDeque.peekLast()]) {
maxDeque.pollLast();
}
maxDeque.offerLast(right);
}
else {
// 最大值与最小值之差大于限制的情况下左指针右移
left = Math.min(minIdx, maxIdx) + 1;
while (minDeque.size() > 0 && minDeque.peekFirst() < left) {
minDeque.pollFirst();
}
while (maxDeque.size() > 0 && maxDeque.peekFirst() < left) {
maxDeque.pollFirst();
}
}
}
return result;
}
}
三角形最小路径和
方法一:动态规划
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[][] f = new int[n][n];
f[0][0] = triangle.get(0).get(0);
for (int i = 1; i < n; ++i) {
f[i][0] = f[i - 1][0] + triangle.get(i).get(0);
for (int j = 1; j < i; ++j) {
f[i][j] = Math.min(f[i - 1][j - 1], f[i - 1][j]) + triangle.get(i).get(j);
}
f[i][i] = f[i - 1][i - 1] + triangle.get(i).get(i);
}
int minTotal = f[n - 1][0];
for (int i = 1; i < n; ++i) {
minTotal = Math.min(minTotal, f[n - 1][i]);
}
return minTotal;
}
}
方法二:动态规划 + 空间优化
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[][] f = new int[2][n];
f[0][0] = triangle.get(0).get(0);
for (int i = 1; i < n; ++i) {
int curr = i % 2;
int prev = 1 - curr;
f[curr][0] = f[prev][0] + triangle.get(i).get(0);
for (int j = 1; j < i; ++j) {
f[curr][j] = Math.min(f[prev][j - 1], f[prev][j]) + triangle.get(i).get(j);
}
f[curr][i] = f[prev][i - 1] + triangle.get(i).get(i);
}
int minTotal = f[(n - 1) % 2][0];
for (int i = 1; i < n; ++i) {
minTotal = Math.min(minTotal, f[(n - 1) % 2][i]);
}
return minTotal;
}
}
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[] f = new int[n];
f[0] = triangle.get(0).get(0);
for (int i = 1; i < n; ++i) {
f[i] = f[i - 1] + triangle.get(i).get(i);
for (int j = i - 1; j > 0; --j) {
f[j] = Math.min(f[j - 1], f[j]) + triangle.get(i).get(j);
}
f[0] += triangle.get(i).get(0);
}
int minTotal = f[0];
for (int i = 1; i < n; ++i) {
minTotal = Math.min(minTotal, f[i]);
}
return minTotal;
}
}
合并两个有序数组
双指针
// 从前往后
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
// Make a copy of nums1.
int [] nums1_copy = new int[m];
System.arraycopy(nums1, 0, nums1_copy, 0, m);
// Two get pointers for nums1_copy and nums2.
int p1 = 0;
int p2 = 0;
// Set pointer for nums1
int p = 0;
// Compare elements from nums1_copy and nums2
// and add the smallest one into nums1.
while ((p1 < m) && (p2 < n))
nums1[p++] = (nums1_copy[p1] < nums2[p2]) ? nums1_copy[p1++] : nums2[p2++];
// if there are still elements to add
if (p1 < m)
System.arraycopy(nums1_copy, p1, nums1, p1 + p2, m + n - p1 - p2);
if (p2 < n)
System.arraycopy(nums2, p2, nums1, p1 + p2, m + n - p1 - p2);
}
}
//从后往前
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
// two get pointers for nums1 and nums2
int p1 = m - 1;
int p2 = n - 1;
// set pointer for nums1
int p = m + n - 1;
// while there are still elements to compare
while ((p1 >= 0) && (p2 >= 0))
// compare two elements from nums1 and nums2
// and add the largest one in nums1
nums1[p--] = (nums1[p1] < nums2[p2]) ? nums2[p2--] : nums1[p1--];
// add missing elements from nums2
System.arraycopy(nums2, 0, nums1, 0, p2 + 1);
}
}
长度最小的子数组
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int ans = Integer.MAX_VALUE;
int start = 0, end = 0;
int sum = 0;
while (end < n) {
sum += nums[end];
while (sum >= s) {
ans = Math.min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
二维数组中的查找
方法一:暴力
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int rows = matrix.length, columns = matrix[0].length;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (matrix[i][j] == target) {
return true;
}
}
}
return false;
}
}
方法二:线性查找
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int rows = matrix.length, columns = matrix[0].length;
int row = 0, column = columns - 1;
while (row < rows && column >= 0) {
int num = matrix[row][column];
if (num == target) {
return true;
} else if (num > target) {
column--;
} else {
row++;
}
}
return false;
}
}
搜索二维矩阵
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
if (m == 0) return false;
int n = matrix[0].length;
// 二分查找
int left = 0, right = m * n - 1;
int pivotIdx, pivotElement;
while (left <= right) {
pivotIdx = (left + right) / 2;
pivotElement = matrix[pivotIdx / n][pivotIdx % n];
if (target == pivotElement) return true;
else {
if (target < pivotElement) right = pivotIdx - 1;
else left = pivotIdx + 1;
}
}
return false;
}
}
斐波那契数
方法一:递归
public class Solution {
public int fib(int N) {
if (N <= 1) {
return N;
}
return fib(N-1) + fib(N-2);
}
}
方法二:记忆化自底向上的方法
class Solution {
public int fib(int N) {
if (N <= 1) {
return N;
}
return memoize(N);
}
public int memoize(int N) {
int[] cache = new int[N + 1];
cache[1] = 1;
for (int i = 2; i <= N; i++) {
cache[i] = cache[i-1] + cache[i-2];
}
return cache[N];
}
}
方法三:记忆化自顶向下的方法
class Solution {
private Integer[] cache = new Integer[31];
public int fib(int N) {
if (N <= 1) {
return N;
}
cache[0] = 0;
cache[1] = 1;
return memoize(N);
}
public int memoize(int N) {
if (cache[N] != null) {
return cache[N];
}
cache[N] = memoize(N-1) + memoize(N-2);
return memoize(N);
}
}
方法四:自底向上进行迭代
class Solution {
public int fib(int N) {
if (N <= 1) {
return N;
}
if (N == 2) {
return 1;
}
int current = 0;
int prev1 = 1;
int prev2 = 1;
for (int i = 3; i <= N; i++) {
current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return current;
}
}
子集
方法一:迭代法实现子集枚举
class Solution {
List<Integer> t = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
int n = nums.length;
for (int mask = 0; mask < (1 << n); ++mask) {
t.clear();
for (int i = 0; i < n; ++i) {
if ((mask & (1 << i)) != 0) {
t.add(nums[i]);
}
}
ans.add(new ArrayList<Integer>(t));
}
return ans;
}
}
方法二:递归法实现子集枚举
class Solution {
List<Integer> t = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
dfs(0, nums);
return ans;
}
public void dfs(int cur, int[] nums) {
if (cur == nums.length) {
ans.add(new ArrayList<Integer>(t));
return;
}
t.add(nums[cur]);
dfs(cur + 1, nums);
t.remove(t.size() - 1);
dfs(cur + 1, nums);
}
}
最短无序连续子数组
方法 1:暴力
public class Solution {
public int findUnsortedSubarray(int[] nums) {
int res = nums.length;
for (int i = 0; i < nums.length; i++) {
for (int j = i; j <= nums.length; j++) {
int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, prev = Integer.MIN_VALUE;
for (int k = i; k < j; k++) {
min = Math.min(min, nums[k]);
max = Math.max(max, nums[k]);
}
if ((i > 0 && nums[i - 1] > min) || (j < nums.length && nums[j] < max))
continue;
int k = 0;
while (k < i && prev <= nums[k]) {
prev = nums[k];
k++;
}
if (k != i)
continue;
k = j;
while (k < nums.length && prev <= nums[k]) {
prev = nums[k];
k++;
}
if (k == nums.length) {
res = Math.min(res, j - i);
}
}
}
return res;
}
}
方法 2:更好的暴力
public class Solution {
public int findUnsortedSubarray(int[] nums) {
int l = nums.length, r = 0;
for (int i = 0; i < nums.length - 1; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[i]) {
r = Math.max(r, j);
l = Math.min(l, i);
}
}
}
return r - l < 0 ? 0 : r - l + 1;
}
}
方法 3:排序
public class Solution {
public int findUnsortedSubarray(int[] nums) {
int[] snums = nums.clone();
Arrays.sort(snums);
int start = snums.length, end = 0;
for (int i = 0; i < snums.length; i++) {
if (snums[i] != nums[i]) {
start = Math.min(start, i);
end = Math.max(end, i);
}
}
return (end - start >= 0 ? end - start + 1 : 0);
}
}
方法 4:使用栈
public class Solution {
public int findUnsortedSubarray(int[] nums) {
Stack < Integer > stack = new Stack < Integer > ();
int l = nums.length, r = 0;
for (int i = 0; i < nums.length; i++) {
while (!stack.isEmpty() && nums[stack.peek()] > nums[i])
l = Math.min(l, stack.pop());
stack.push(i);
}
stack.clear();
for (int i = nums.length - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] < nums[i])
r = Math.max(r, stack.pop());
stack.push(i);
}
return r - l > 0 ? r - l + 1 : 0;
}
}
方法 5:不使用额外空间
public class Solution {
public int findUnsortedSubarray(int[] nums) {
int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
boolean flag = false;
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1])
flag = true;
if (flag)
min = Math.min(min, nums[i]);
}
flag = false;
for (int i = nums.length - 2; i >= 0; i--) {
if (nums[i] > nums[i + 1])
flag = true;
if (flag)
max = Math.max(max, nums[i]);
}
int l, r;
for (l = 0; l < nums.length; l++) {
if (min < nums[l])
break;
}
for (r = nums.length - 1; r >= 0; r--) {
if (max > nums[r])
break;
}
return r - l < 0 ? 0 : r - l + 1;
}
}
最小路径和
class Solution {
public int minPathSum(int[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) {
return 0;
}
int rows = grid.length, columns = grid[0].length;
int[][] dp = new int[rows][columns];
dp[0][0] = grid[0][0];
for (int i = 1; i < rows; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < columns; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for (int i = 1; i < rows; i++) {
for (int j = 1; j < columns; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[rows - 1][columns - 1];
}
}
矩阵置零
方法 1:额外存储空间方法
class Solution {
public void setZeroes(int[][] matrix) {
int R = matrix.length;
int C = matrix[0].length;
Set<Integer> rows = new HashSet<Integer>();
Set<Integer> cols = new HashSet<Integer>();
// Essentially, we mark the rows and columns that are to be made zero
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (matrix[i][j] == 0) {
rows.add(i);
cols.add(j);
}
}
}
// Iterate over the array once again and using the rows and cols sets, update the elements.
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (rows.contains(i) || cols.contains(j)) {
matrix[i][j] = 0;
}
}
}
}
}
方法 2:O(1)空间的暴力
class Solution {
public void setZeroes(int[][] matrix) {
int MODIFIED = -1000000;
int R = matrix.length;
int C = matrix[0].length;
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
if (matrix[r][c] == 0) {
// We modify the corresponding rows and column elements in place.
// Note, we only change the non zeroes to MODIFIED
for (int k = 0; k < C; k++) {
if (matrix[r][k] != 0) {
matrix[r][k] = MODIFIED;
}
}
for (int k = 0; k < R; k++) {
if (matrix[k][c] != 0) {
matrix[k][c] = MODIFIED;
}
}
}
}
}
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
// Make a second pass and change all MODIFIED elements to 0 """
if (matrix[r][c] == MODIFIED) {
matrix[r][c] = 0;
}
}
}
}
}
转变数组后最接近目标值的数组和
方法一:枚举 + 二分查找
class Solution {
public int findBestValue(int[] arr, int target) {
Arrays.sort(arr);
int n = arr.length;
int[] prefix = new int[n + 1];
for (int i = 1; i <= n; ++i) {
prefix[i] = prefix[i - 1] + arr[i - 1];
}
int r = arr[n - 1];
int ans = 0, diff = target;
for (int i = 1; i <= r; ++i) {
int index = Arrays.binarySearch(arr, i);
if (index < 0) {
index = -index - 1;
}
int cur = prefix[index] + (n - index) * i;
if (Math.abs(cur - target) < diff) {
ans = i;
diff = Math.abs(cur - target);
}
}
return ans;
}
}
方法二:双重二分查找
class Solution {
public int findBestValue(int[] arr, int target) {
Arrays.sort(arr);
int n = arr.length;
int[] prefix = new int[n + 1];
for (int i = 1; i <= n; ++i) {
prefix[i] = prefix[i - 1] + arr[i - 1];
}
int l = 0, r = arr[n - 1], ans = -1;
while (l <= r) {
int mid = (l + r) / 2;
int index = Arrays.binarySearch(arr, mid);
if (index < 0) {
index = -index - 1;
}
int cur = prefix[index] + (n - index) * mid;
if (cur <= target) {
ans = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
int chooseSmall = check(arr, ans);
int chooseBig = check(arr, ans + 1);
return Math.abs(chooseSmall - target) <= Math.abs(chooseBig - target) ? ans : ans + 1;
}
public int check(int[] arr, int x) {
int ret = 0;
for (int num : arr) {
ret += Math.min(num, x);
}
return ret;
}
}
两数之和 II - 输入有序数组
方法一:二分查找
class Solution {
public int[] twoSum(int[] numbers, int target) {
for (int i = 0; i < numbers.length; ++i) {
int low = i + 1, high = numbers.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] == target - numbers[i]) {
return new int[]{i + 1, mid + 1};
} else if (numbers[mid] > target - numbers[i]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
}
return new int[]{-1, -1};
}
}
方法二:双指针
class Solution {
public int[] twoSum(int[] numbers, int target) {
int low = 0, high = numbers.length - 1;
while (low < high) {
int sum = numbers[low] + numbers[high];
if (sum == target) {
return new int[]{low + 1, high + 1};
} else if (sum < target) {
++low;
} else {
--high;
}
}
return new int[]{-1, -1};
}
}
寻找旋转排序数组中的最小值
二分搜索
class Solution {
public int findMin(int[] nums) {
// If the list has just one element then return that element.
if (nums.length == 1) {
return nums[0];
}
// initializing left and right pointers.
int left = 0, right = nums.length - 1;
// if the last element is greater than the first element then there is no rotation.
// e.g. 1 < 2 < 3 < 4 < 5 < 7. Already sorted array.
// Hence the smallest element is first element. A[0]
if (nums[right] > nums[0]) {
return nums[0];
}
// Binary search way
while (right >= left) {
// Find the mid element
int mid = left + (right - left) / 2;
// if the mid element is greater than its next element then mid+1 element is the smallest
// This point would be the point of change. From higher to lower value.
if (nums[mid] > nums[mid + 1]) {
return nums[mid + 1];
}
// if the mid element is lesser than its previous element then mid element is the smallest
if (nums[mid - 1] > nums[mid]) {
return nums[mid];
}
// if the mid elements value is greater than the 0th element this means
// the least value is still somewhere to the right as we are still dealing with elements
// greater than nums[0]
if (nums[mid] > nums[0]) {
left = mid + 1;
} else {
// if nums[0] is greater than the mid value then this means the smallest value is somewhere to
// the left
right = mid - 1;
}
}
return -1;
}
}
数组中的 k 个最强值
排序 + 双指针
class Solution {
public int[] getStrongest(int[] arr, int k) {
Arrays.sort(arr); // 将数组按数值从小到大排序
int mid = arr[(arr.length - 1) / 2],index = 0;
// mid为数组的中位数,注意题目中定义的中位数与“一般定义”不同
int[] ans = new int[k];
int left = 0,right = arr.length - 1;
while(index < k) { // 直到获取k个数为止
int a = Math.abs(arr[left] - mid),b = Math.abs(arr[right] - mid);
/* 按规则获取数字 */
if(a > b) {
ans[index] = arr[left];left++;index++;
}
else { // 由于数组中的数字按升序排序,因此右边的数总是大于左边的
ans[index] = arr[right];right--;index++;
}
}
return ans;
}
}
寻找重复数
方法一:二分查找
class Solution {
public int findDuplicate(int[] nums) {
int n = nums.length;
int l = 1, r = n - 1, ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
int cnt = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] <= mid) {
cnt++;
}
}
if (cnt <= mid) {
l = mid + 1;
} else {
r = mid - 1;
ans = mid;
}
}
return ans;
}
}
方法二:快慢指针
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0, fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
乘积最大子数组
动态规划
//动态方程
class Solution {
public int maxProduct(int[] nums) {
int length = nums.length;
int[] maxF = new int[length];
int[] minF = new int[length];
System.arraycopy(nums, 0, maxF, 0, length);
System.arraycopy(nums, 0, minF, 0, length);
for (int i = 1; i < length; ++i) {
maxF[i] = Math.max(maxF[i - 1] * nums[i], Math.max(nums[i], minF[i - 1] * nums[i]));
minF[i] = Math.min(minF[i - 1] * nums[i], Math.min(nums[i], maxF[i - 1] * nums[i]));
}
int ans = maxF[0];
for (int i = 1; i < length; ++i) {
ans = Math.max(ans, maxF[i]);
}
return ans;
}
}
//空间优化
class Solution {
public int maxProduct(int[] nums) {
int maxF = nums[0], minF = nums[0], ans = nums[0];
int length = nums.length;
for (int i = 1; i < length; ++i) {
int mx = maxF, mn = minF;
maxF = Math.max(mx * nums[i], Math.max(nums[i], mn * nums[i]));
minF = Math.min(mn * nums[i], Math.min(nums[i], mx * nums[i]));
ans = Math.max(maxF, ans);
}
return ans;
}
}
搜索插入位置
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
}
删除排序数组中的重复项
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
最长连续序列
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> num_set = new HashSet<Integer>();
for (int num : nums) {
num_set.add(num);
}
int longestStreak = 0;
for (int num : num_set) {
if (!num_set.contains(num - 1)) {
int currentNum = num;
int currentStreak = 1;
while (num_set.contains(currentNum + 1)) {
currentNum += 1;
currentStreak += 1;
}
longestStreak = Math.max(longestStreak, currentStreak);
}
}
return longestStreak;
}
}
最长的斐波那契子序列的长度
方法二:动态规划
class Solution {
public int lenLongestFibSubseq(int[] A) {
int N = A.length;
Map<Integer, Integer> index = new HashMap();
for (int i = 0; i < N; ++i)
index.put(A[i], i);
Map<Integer, Integer> longest = new HashMap();
int ans = 0;
for (int k = 0; k < N; ++k)
for (int j = 0; j < k; ++j) {
int i = index.getOrDefault(A[k] - A[j], -1);
if (i >= 0 && i < j) {
// Encoding tuple (i, j) as integer (i * N + j)
int cand = longest.getOrDefault(i * N + j, 2) + 1;
longest.put(j * N + k, cand);
ans = Math.max(ans, cand);
}
}
return ans >= 3 ? ans : 0;
}
}
盛最多水的容器
public class Solution {
public int maxArea(int[] height) {
int l = 0, r = height.length - 1;
int ans = 0;
while (l < r) {
int area = Math.min(height[l], height[r]) * (r - l);
ans = Math.max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
--r;
}
}
return ans;
}
}
使用最小花费爬楼梯
class Solution {
public int minCostClimbingStairs(int[] cost) {
int f1 = 0, f2 = 0;
for (int i = cost.length - 1; i >= 0; --i) {
int f0 = cost[i] + Math.min(f1, f2);
f2 = f1;
f1 = f0;
}
return Math.min(f1, f2);
}
}
最长湍流子数组
class Solution {
public int maxTurbulenceSize(int[] A) {
int N = A.length;
int ans = 1;
int anchor = 0;
for (int i = 1; i < N; ++i) {
int c = Integer.compare(A[i-1], A[i]);
if (i == N-1 || c * Integer.compare(A[i], A[i+1]) != -1) {
if (c != 0) ans = Math.max(ans, i - anchor + 1);
anchor = i;
}
}
return ans;
}
}
最大子序和
方法一:动态规划
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0, maxAns = nums[0];
for (int x : nums) {
pre = Math.max(pre + x, x);
maxAns = Math.max(maxAns, pre);
}
return maxAns;
}
}
方法二:分治
class Solution {
public class Status {
public int lSum, rSum, mSum, iSum;
public Status(int lSum, int rSum, int mSum, int iSum) {
this.lSum = lSum;
this.rSum = rSum;
this.mSum = mSum;
this.iSum = iSum;
}
}
public int maxSubArray(int[] nums) {
return getInfo(nums, 0, nums.length - 1).mSum;
}
public Status getInfo(int[] a, int l, int r) {
if (l == r) {
return new Status(a[l], a[l], a[l], a[l]);
}
int m = (l + r) >> 1;
Status lSub = getInfo(a, l, m);
Status rSub = getInfo(a, m + 1, r);
return pushUp(lSub, rSub);
}
public Status pushUp(Status l, Status r) {
int iSum = l.iSum + r.iSum;
int lSum = Math.max(l.lSum, l.iSum + r.lSum);
int rSum = Math.max(r.rSum, r.iSum + l.rSum);
int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
return new Status(lSum, rSum, mSum, iSum);
}
}
最长重复子数组
方法一:动态规划
class Solution {
public int findLength(int[] A, int[] B) {
int n = A.length, m = B.length;
int[][] dp = new int[n + 1][m + 1];
int ans = 0;
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
dp[i][j] = A[i] == B[j] ? dp[i + 1][j + 1] + 1 : 0;
ans = Math.max(ans, dp[i][j]);
}
}
return ans;
}
}
方法二:滑动窗口
class Solution {
public int findLength(int[] A, int[] B) {
int n = A.length, m = B.length;
int ret = 0;
for (int i = 0; i < n; i++) {
int len = Math.min(m, n - i);
int maxlen = maxLength(A, B, i, 0, len);
ret = Math.max(ret, maxlen);
}
for (int i = 0; i < m; i++) {
int len = Math.min(n, m - i);
int maxlen = maxLength(A, B, 0, i, len);
ret = Math.max(ret, maxlen);
}
return ret;
}
public int maxLength(int[] A, int[] B, int addA, int addB, int len) {
int ret = 0, k = 0;
for (int i = 0; i < len; i++) {
if (A[addA + i] == B[addB + i]) {
k++;
} else {
k = 0;
}
ret = Math.max(ret, k);
}
return ret;
}
}
方法三:二分查找 + 哈希
class Solution {
int mod = 1000000009;
int base = 113;
public int findLength(int[] A, int[] B) {
int left = 1, right = Math.min(A.length, B.length) + 1;
while (left < right) {
int mid = (left + right) >> 1;
if (check(A, B, mid)) {
left = mid + 1;
} else {
right = mid;
}
}
return left - 1;
}
public boolean check(int[] A, int[] B, int len) {
long hashA = 0;
for (int i = 0; i < len; i++) {
hashA = (hashA * base + A[i]) % mod;
}
Set<Long> bucketA = new HashSet<Long>();
bucketA.add(hashA);
long mult = qPow(base, len - 1);
for (int i = len; i < A.length; i++) {
hashA = ((hashA - A[i - len] * mult % mod + mod) % mod * base + A[i]) % mod;
bucketA.add(hashA);
}
long hashB = 0;
for (int i = 0; i < len; i++) {
hashB = (hashB * base + B[i]) % mod;
}
if (bucketA.contains(hashB)) {
return true;
}
for (int i = len; i < B.length; i++) {
hashB = ((hashB - B[i - len] * mult % mod + mod) % mod * base + B[i]) % mod;
if (bucketA.contains(hashB)) {
return true;
}
}
return false;
}
// 使用快速幂计算 x^n % mod 的值
public long qPow(long x, long n) {
long ret = 1;
while (n != 0) {
if ((n & 1) != 0) {
ret = ret * x % mod;
}
x = x * x % mod;
n >>= 1;
}
return ret;
}
}
岛屿的最大面积
方法一:深度优先搜索
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; ++i) {
for (int j = 0; j != grid[0].length; ++j) {
ans = Math.max(ans, dfs(grid, i, j));
}
}
return ans;
}
public int dfs(int[][] grid, int cur_i, int cur_j) {
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || grid[cur_i][cur_j] != 1) {
return 0;
}
grid[cur_i][cur_j] = 0;
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
int ans = 1;
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
ans += dfs(grid, next_i, next_j);
}
return ans;
}
}
方法二:深度优先搜索 + 栈
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; ++i) {
for (int j = 0; j != grid[0].length; ++j) {
int cur = 0;
Deque<Integer> stacki = new LinkedList<Integer>();
Deque<Integer> stackj = new LinkedList<Integer>();
stacki.push(i);
stackj.push(j);
while (!stacki.isEmpty()) {
int cur_i = stacki.pop(), cur_j = stackj.pop();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
stacki.push(next_i);
stackj.push(next_j);
}
}
ans = Math.max(ans, cur);
}
}
return ans;
}
}
方法三:广度优先搜索
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; ++i) {
for (int j = 0; j != grid[0].length; ++j) {
int cur = 0;
Queue<Integer> queuei = new LinkedList<Integer>();
Queue<Integer> queuej = new LinkedList<Integer>();
queuei.offer(i);
queuej.offer(j);
while (!queuei.isEmpty()) {
int cur_i = queuei.poll(), cur_j = queuej.poll();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
queuei.offer(next_i);
queuej.offer(next_j);
}
}
ans = Math.max(ans, cur);
}
}
return ans;
}
}
组合总和
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> combine = new ArrayList<Integer>();
dfs(candidates, target, ans, combine, 0);
return ans;
}
public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
if (idx == candidates.length) {
return;
}
if (target == 0) {
ans.add(new ArrayList<Integer>(combine));
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.add(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.remove(combine.size() - 1);
}
}
}
颜色分类
方法一:单指针
class Solution {
public void sortColors(int[] nums) {
int n = nums.length;
int ptr = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 0) {
int temp = nums[i];
nums[i] = nums[ptr];
nums[ptr] = temp;
++ptr;
}
}
for (int i = ptr; i < n; ++i) {
if (nums[i] == 1) {
int temp = nums[i];
nums[i] = nums[ptr];
nums[ptr] = temp;
++ptr;
}
}
}
}
方法二:双指针
class Solution {
public void sortColors(int[] nums) {
int n = nums.length;
int p0 = 0, p1 = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 1) {
int temp = nums[i];
nums[i] = nums[p1];
nums[p1] = temp;
++p1;
} else if (nums[i] == 0) {
int temp = nums[i];
nums[i] = nums[p0];
nums[p0] = temp;
if (p0 < p1) {
temp = nums[i];
nums[i] = nums[p1];
nums[p1] = temp;
}
++p0;
++p1;
}
}
}
}
方法三:双指针
class Solution {
public void sortColors(int[] nums) {
int n = nums.length;
int p0 = 0, p2 = n - 1;
for (int i = 0; i <= p2; ++i) {
while (i <= p2 && nums[i] == 2) {
int temp = nums[i];
nums[i] = nums[p2];
nums[p2] = temp;
--p2;
}
if (nums[i] == 0) {
int temp = nums[i];
nums[i] = nums[p0];
nums[p0] = temp;
++p0;
}
}
}
}
接雨水
方法 1:暴力
public int trap(int[] height) {
int ans = 0;
int size = height.length;
for (int i = 1; i < size - 1; i++) {
int max_left = 0, max_right = 0;
for (int j = i; j >= 0; j--) { //Search the left part for max bar size
max_left = Math.max(max_left, height[j]);
}
for (int j = i; j < size; j++) { //Search the right part for max bar size
max_right = Math.max(max_right, height[j]);
}
ans += Math.min(max_left, max_right) - height[i];
}
return ans;
}
方法 2:动态编程
public int trap(int[] height) {
if (height == null || height.length == 0)
return 0;
int ans = 0;
int size = height.length;
int[] left_max = new int[size];
int[] right_max = new int[size];
left_max[0] = height[0];
for (int i = 1; i < size; i++) {
left_max[i] = Math.max(height[i], left_max[i - 1]);
}
right_max[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--) {
right_max[i] = Math.max(height[i], right_max[i + 1]);
}
for (int i = 1; i < size - 1; i++) {
ans += Math.min(left_max[i], right_max[i]) - height[i];
}
return ans;
}
方法 3:栈的应用
public int trap(int[] height) {
int ans = 0, current = 0;
Deque<Integer> stack = new LinkedList<Integer>();
while (current < height.length) {
while (!stack.isEmpty() && height[current] > height[stack.peek()]) {
int top = stack.pop();
if (stack.isEmpty())
break;
int distance = current - stack.peek() - 1;
int bounded_height = Math.min(height[current], height[stack.peek()]) - height[top];
ans += distance * bounded_height;
}
stack.push(current++);
}
return ans;
}
方法 4:使用双指针
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int ans = 0;
int left_max = 0, right_max = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= left_max) {
left_max = height[left];
} else {
ans += (left_max - height[left]);
}
++left;
} else {
if (height[right] >= right_max) {
right_max = height[right];
} else {
ans += (right_max - height[right]);
}
--right;
}
}
return ans;
}