String的值是不可变的,导致每次对String的操作都会生成新的String对象,效率低下,而且浪费有限的内存空间。
StringBuilder和StringBuffer类的对象能够被多次的修改,并且不产生新的未使用对象
StringBuilder类在Java5中被提出,和StringBuffer之间的最大不同在于StringBuilder的方法不是线程安全的(不能同步访问)
StringBuilder比StringBuffer有速度优势,所以多数情况下使用StringBuilder类。在应用程序要求线程安全的情况下,必须使用StringBuffer类
String可以空赋值,其他两种不行
操作少量数据可以使用String
数组,栈,链表,队列,树,图,堆,散列表等
数组:在内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组的下标进行访问。数组的查询比较方便,但是添加和删除操作比较慢,需要移动其他的元素,固定大小
栈:先进先出,入栈,出栈,栈通常用于递归功能的实现比如斐波那契数列
队列:先进先出,在多线程阻塞队列管理中比较适用
链表:物理存储单元上非连续的、非顺序的存储结果,逻辑顺序通过链表的指针地址实现,包含数据域和指针域。链表可以任意加减元素,添加和删除操作比较快。但是因为包含大量的指针域,占用的空间比较大。查找元素耗时,适合数据量小,频繁增加,删除操作
二叉树:添加,删除元素都很快,在查找方面也有很多的算法优化,二叉树既有链表的好处,也有数组的好处。处理大批量的动态数据。二叉树有很多扩展的数据结构,包括平衡二叉树,红黑树,B+树等。Mysql的数据库索引结构使用的是B+树,HashMap的底层源码中使用红黑树
散列表:哈希表,key value进行访问的数据结构,通过key和value来映射到集合中的一个位置,很快就可以找到集合中对应的元素。Java中有些集合类就是借鉴的哈希原理进行构造。比如HashMap和HashTable等,hash表对于集合的查找元素是非常方便的,因为哈希表是基于数组的,在添加和删除元素比较慢。进一步就是数组结合链表,hashMap使用数组+链表+红黑树
堆:完全二叉树,大根堆和小根堆,一般用来做数组中的排序,称为堆排序
图:节点的又穷集合和边的结合,无向图和有向图。在存储数据上有比较复杂和高效的算法,分别有邻接举证、邻接表、十字链表、邻接多重表、边集数组等存储结构
private static void QuickSort1(int[] nums, int left, int right) {
int f = nums[left];
int i = left, j = right;
if (i >= j)
return;
while (i < j) {
while (f <= nums[j] && i < j) {
j--;
}
nums[i] = nums[j];
while (f >= nums[i] && i < j) {
i++;
}
nums[j] = nums[i];
}
nums[i] = f;
QuickSort1(nums, left, i - 1);
QuickSort1(nums, i + 1, right);
}
时间复杂度:在拆分时,极端情况:基准数左边的元素个数都为0,而右边n-1个。这个时候,就需要拆分n次,每次拆分整理的时间复杂度为n,最坏的时间复杂度为n2。C(n)=(n-1)+(n-2)+…+1=n(n-1)/2。最坏情况下,快速排序的时间复杂度为n2
最好的情况下就是每次拆分都能够从书序的中间拆分,拆分logn次,时间复杂度为nlogn
在随机取基准数的时候,数据是可能会发生变化的,快速排序不是稳定的
// 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
//
// 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
//
//
//
// 示例 1:
//
//
// 输入: [3,2,1,5,6,4] 和 k = 2
// 2 1 3 5 6 4
// 输出: 5
//
//
// 示例 2:
//
//
// 输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
// 输出: 4
//
//
//
// 提示:
//
//
// 1 <= k <= nums.length <= 10⁴
// -10⁴ <= nums[i] <= 10⁴
//
// Related Topics 数组 分治 快速选择 排序 堆(优先队列) 1460 0
package leetcode.editor.cn;
// Java:数组中的第K个最大元素
public class KthLargestElementInAnArray {
public static void main(String[] args) {
Solution solution = new KthLargestElementInAnArray().new Solution();
// TO TEST
System.out.println(solution.findKthLargest(new int[] {3, 2, 3, 1, 2, 4, 5, 5, 6}, 4));
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
// 快速排序
public int findKthLargest(int[] nums, int k) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int f = sort(nums, left, right);
if (f == k - 1) {
return nums[f];
}
if (f > k - 1) {
right = f - 1;
} else {
left = f + 1;
}
}
return -1;
}
public int sort(int[] nums, int left, int right) {
int key = nums[right];
int i = left, j = right;
while (i < j) {
while (i < j && key <= nums[i]) {
i++;
}
nums[j] = nums[i];
while (i < j && key >= nums[j]) {
j--;
}
nums[i] = nums[j];
}
nums[i] = key;
return i;
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
// 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
//
//
//
// 示例 1:
//
//
// 输入: nums = [1,1,1,2,2,3], k = 2
// 输出: [1,2]
//
//
// 示例 2:
//
//
// 输入: nums = [1], k = 1
// 输出: [1]
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 10⁵
// k 的取值范围是 [1, 数组中不相同的元素的个数]
// 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
//
//
//
//
// 进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
// Related Topics 数组 哈希表 分治 桶排序 计数 快速选择 排序 堆(优先队列) 990 0
package leetcode.editor.cn;
import java.util.*;
// Java:前 K 个高频元素
public class TopKFrequentElements {
public static void main(String[] args) {
Solution solution = new TopKFrequentElements().new Solution();
// TO TEST
System.out.println(Arrays.toString(solution.topKFrequent(new int[] {-1, 1, 4, -4, 3, 5, 4, -2, 3, -1}, 3)));
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
/**
* 使用HashMap存储元素和出现次数,重写排序比较方法
*/
public int[] topKFrequent1(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
ArrayList<Map.Entry<Integer, Integer>> entries = new ArrayList<>(map.entrySet());
entries.sort(new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
int[] res = new int[k];
Map.Entry<Integer, Integer> entry;
for (int i = 0; i < k; i++) {
res[i] = entries.get(i).getKey();
}
return res;
}
/*
* 堆
* 1.如果堆的元素个数小于k,直接插入堆中
* 2.如果堆的个数等于k,检查堆顶与当前出现次数大小,堆顶更大就舍弃当前值,堆顶更小就替换
*/
/*
* 使用hashmap存储元素出现的次数,使用快速排序求前k个最大的,时间复杂度为n
*/
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
ArrayList<Map.Entry<Integer, Integer>> entries = new ArrayList<>(map.entrySet());
List<int[]> list = new LinkedList<int[]>();
for (Map.Entry<Integer, Integer> entry : entries) {
list.add(new int[] {entry.getKey(), entry.getValue()});
}
// 快速排序,只需要前k个
int left = 0, right = list.size() - 1;
while (left <= right) {
int tmp = QSort(list, left, right);
if (tmp == k - 1) {
break;
} else if (tmp > k - 1) {
right = tmp - 1;
} else {
left = tmp + 1;
}
}
int[] res = new int[k];
for (int i = 0; i < k; i++) {
res[i] = list.get(i)[0];
}
return res;
}
private int QSort(List<int[]> list, int left, int right) {
int i = left, j = right;
int key = right;
// 防止覆盖
int[] num = {list.get(key)[0], list.get(key)[1]};
int k = list.get(key)[1];
while (i < j) {
while (i < j && k <= list.get(i)[1]) {
i++;
}
list.set(j, new int[] {list.get(i)[0], list.get(i)[1]});
while (i < j && k >= list.get(j)[1]) {
j--;
}
list.set(i, new int[] {list.get(j)[0], list.get(j)[1]});
}
list.set(i, num);
return i;
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
/**
* 冒泡排序
* 1.从数组头开始,比较相邻的元素,如果第一个元素比第二个大,就交换他们
* 2.对每一对相邻元素做相同的工作
* 3.重复
*/
public static void BubbleSort(int[] nums){
if(nums.length == 0) return;
for (int i = 0; i < nums.length-1; i++) {
for (int j = 0; j < nums.length-i-1; j++) {
if(nums[j]>nums[j+1]){
int tmp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = tmp;
}
}
}
}
时间复杂度为n^2
该算法在比较两个相同的数时没必要进行交换,是稳定的算法
/**
* 选择排序
* 1.找到数组中最大或者最小的元素
* 2.将它和数组的第一个元素交换位置,如果第一个元素就是最小的元素,它就和自己交换
* 3.在剩下的元素中找到最大或者最小的元素,将它和数组的第二个元素交换位置,往复
*/
public static void ChooseSort(int[] nums){
if(nums.length == 0) return;
for (int i = 0; i < nums.length; i++) {
int min = i;
for (int j = i+1; j < nums.length; j++) {
//找到后面最小的元素
if(nums[j]<nums[min]){
min = j;
}
}
//交换
int tmp = nums[min];
nums[min] = nums[i];
nums[i] = tmp;
}
}
时间复杂度 n^2
如果选择的元素比当前的元素小,剩下的元素中有和指定的元素相同的元素,破坏了稳定性。数组5,8,5,2,9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了
/**
* 插入排序
* 1.对于未排序的元素,在已排序序列中从左往右扫描,找到相应位置并插入
* 2.为了给要插入的元素腾出空间,需要将插入位置之后的已排序元素都向后移动一位
* @param
*/
public static void InsertSort(int[] nums){
if(nums.length == 0) return;
//待排序的数据,该数据之前的已被排序
int current;
for (int i = 0; i < nums.length-1; i++) {
//已被排序数据的做引
int index = i;
current = nums[index+1];
//将当前元素后移一位
while(index >= 0 && current < nums[index]){
nums[index+1] = nums[index];
index--;
}
//插入元素
nums[index + 1] = current;
}
}
时间复杂度:最坏的情况下为n^2 最好的情况下为n
比较的时候发现,两个数相等的话,不会进行移动,前后两个数都是相等的,该算法是稳定的算法
/**
* 希尔排序
* 基于插入排序的快速的排序算法
* 简单插入排序对于大规模乱序数组很慢
* 希尔排序为了加速简单的改进了插入排序,也称为缩小增量排序
* 希尔排序时吧待排序数组按一定的数量分组,对每组使用直接插入排序算法排序,然后缩小数量继续分组排序,随着数量的减少,每组包含的元素越来越多,当数量减至1是,整个数组恰被分为一组,排序便完成了,这个不断缩小的数量构成了一个增量序列
* 4,5,3,7,6,3,2,1
* 4 3 2 1 6 5 3 7
* 2 1 3 3 4 5 6 7
* 1 2 3 3 4 5 6 7
* @param
*/
public static void ShellSort(int[] nums){
int len = nums.length;
if(len<2){
return;
}
//当前待排序数据,该数据之前的已被排序
int current;
//增量
int gap = len / 2;
while(gap > 0){
for (int i = gap; i < len; i++) {
current = nums[i];
//前面有序序列的索引
int index = i - gap;
while(index >= 0 && current < nums[index]){
nums[index + gap] = nums[index];
//有序序列的下一个
index -= gap;
}
//插入
nums[index + gap] = current;
}
//index相除取整
gap = gap / 2;
}
}
时间复杂度:和增量序列有关{n/2,(n/2)/2,…,1}
稳定性:插入排序时稳定的,但是在不同的插入排序中,相同元素可能在各自的插入排序中移动,5,2,2,1,第一次排序第一个元素5会和第三个元素2交换,第二个元素2会和第四个元素1交换,原序列中两个2的相对前后顺序就被破坏
不稳定
堆 快速排序
//输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
//
//
//
// 示例 1:
//
// 输入:arr = [3,2,1], k = 2
//输出:[1,2] 或者 [2,1]
//
//
// 示例 2:
//
// 输入:arr = [0,1,2,1], k = 1
//输出:[0]
//
//
//
// 限制:
//
//
// 0 <= k <= arr.length <= 10000
// 0 <= arr[i] <= 10000
//
// Related Topics 堆 分治算法
// 242 0
package leetcode.editor.cn;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
//Java:最小的k个数
public class ZuiXiaoDeKgeShuLcof{
public static void main(String[] args) {
Solution solution = new ZuiXiaoDeKgeShuLcof().new Solution();
// TO TEST
int[] arr={0,0,2,3,2,1,1,2,0,4};
// Sort(arr,0,arr.length-1);
arr=solution.getLeastNumbers1(arr,5);
// solution.Sort(arr,0,arr.length-1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
/**
* 排序后进行输出,快排
* @param arr
* @param k
* @return
*/
public int[] getLeastNumbers1(int[] arr, int k) {
return QuikSearch(arr,k,0,arr.length-1);
}
public int[] QuikSearch(int[] arr, int k,int left,int right){
//每快排一次,找到排序后下标为i的元素
//k
//k>j 遍历数组j+1~right的元素
//相等,返回数组
int j=QuikSort(arr,left,right);
if(k==j){
return Arrays.copyOf(arr,k);
}
return k>j?QuikSearch(arr,k,j+1,right):QuikSearch(arr,k,left,j-1);
}
/**
* 使用最后一个数作为基准值
* @param arr
* @param left
* @param right
*/
public int QuikSort(int[] arr,int left, int right){
if(left>=right){
return left;
}
int i=left,j=right;
//元素基准值
int key=arr[right];
while(i<j){
while(arr[j]>=key&&i<j){
j--;
}
//基准位置放j位置的值
if(i<j) arr[i++]=arr[j];
while(arr[i]<key&&i<j){
i++;
}
//j位置放置i的值
if(i<j) arr[j--]=arr[i];
}
//i位置放置基准值
arr[i]=key;
return i;
}
/**
* 使用快排会超时
* @param arr
* @param left
* @param right
*/
public void Sort(int[] arr, int left, int right) {
int i=left,j=right;
//当左边大于右边元素时
if(i>=j){
return;
}
//元素基准值
int key=arr[left];
while(i<j){
while(arr[j]>key&&i<j){
j--;
}
//基准位置放j位置的值
arr[i++]=arr[j];
while(arr[i]<key&&i<j){
i++;
}
//j位置放置i的值
arr[j--]=arr[i];
}
//i位置放置基准值
arr[i]=key;
//左边元素进行递归
Sort(arr,left,i-1);
//右边元素进行递归
Sort(arr,j+1,right);
}
// n 1
/**
*大根堆(前K小)/小根堆(前K大)
* 用一个容量为K的大根堆,每次poll出最大的数
* 1.若目前堆的大小为k,将当前数字放入堆中
* 2.否则判断当前元素与大根堆对丁元素的大小关系,如果当前元素比大根堆堆顶大,直接跳过
* 反之,如果当前数字比大根堆堆顶小,先poll掉堆顶,再将该数字放入堆中
* @param arr
* @param k
* @return
*/
public int[] getLeastNumbers(int[] arr, int k)
{
if(k==0||arr.length==0){
return new int[0];
}
//默认是小根堆,大根堆需要重写比较器
Queue<Integer> pq=new PriorityQueue<>((v1,v2)->v2-v1);
for (int num:arr) {
if(pq.size()<k){
pq.offer(num);
}else if(num<pq.peek()){
pq.poll();
pq.offer(num);
}
}
//返回堆中的元素
int[] res=new int[pq.size()];
int idx=0;
for (int num:pq) {
res[idx++]=num;
}
return res;
}
// n nlogn
}
//leetcode submit region end(Prohibit modification and deletion)
}
/**
* 归并排序
* 分而治之,按照设定的与之进行分解成多个计算,然后将各个计算结果进行汇总
* 若将两个有序集合合并成一个有序表,称为2-路归并
*
* @param
*/
public static int[] MergeSort(int[] nums){
if(nums.length < 2) return nums;
int mid = nums.length / 2;
//分成两组
int[] left = Arrays.copyOfRange(nums,0,mid);
int[] right = Arrays.copyOfRange(nums,mid,nums.length);
//递归拆分
return merge(MergeSort(left),MergeSort(right));
}
public static int[] merge(int[] left,int[] right){
int[] result = new int[left.length+right.length];
//i表示左边数组的索引,j表示右边
for (int index = 0,i = 0,j = 0; index < result.length; index++) {
if(i >= left.length){
//说明左侧的数据已经全部取完,取右边的数据
result[index] = right[j++];
}else if(j >= right.length){
//说明右侧的数据已经全部取完,取左边的数据
result[index] = left[i++];
}else if(left[i] > right[j]){
//左边大于右边,取右边的
int a = right[j++];
result[index] = a;
}else{
//右边大于左边,取左边的
result[index] = left[i++];
}
}
return result;
}
时间复杂度:总时间=分解时间+子序列排序时间+合并时间,分解时间是一个常数,可以忽略不计 nlogn
稳定性:在合并的时候,如果相等,选择前面的元素到辅助数组,所以归并排序时稳定的
/**
* 堆排序,这里的堆是一种特殊的二叉树,通常叫做二叉堆
* 是完全二叉树,堆中某个节点的值总是不大于或不小于其父节点的值
* 二叉堆:
* 大根堆:每一个根节点都大于等于它的左右孩子节点,叫最大堆
* 小根堆:每一个根节点都小于等于它的左右孩子节点,叫最小堆
*
* @param args
*/
时间复杂度nlogn
不稳定
非比较排序
计数排序可以看成每个桶值存储相同元素,桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中
找出数组中的最大值max和最小值min,可以确定数组所在范围min-max
根据数据范围确定桶的数量
桶的数量由自己确定,尽量让元素平均分布到每一个桶中 (最大值-最小值)/每个桶所能放置多少个不同数值+1
确定桶的区间,一般是按照(最大值-最小值)/桶的数量划分,左闭右开
按照从右往左的顺序,依次将每一位都当做一次关键字,然后按照该关键字对数组排序,同时每一轮排序都基于上轮排序后的结果,当我们将所有的位排序后,整个数组达到有序状态。基数排序不是基于比较的算法
都是稳定的
// 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
//
// 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
//
//
//
//
//
//
// 示例 1:
//
//
// 输入:nums = [2,0,2,1,1,0]
// 输出:[0,0,1,1,2,2]
//
//
// 示例 2:
//
//
// 输入:nums = [2,0,1]
// 输出:[0,1,2]
//
//
// 示例 3:
//
//
// 输入:nums = [0]
// 输出:[0]
//
//
// 示例 4:
//
//
// 输入:nums = [1]
// 输出:[1]
//
//
//
//
// 提示:
//
//
// n == nums.length
// 1 <= n <= 300
// nums[i] 为 0、1 或 2
//
//
//
//
// 进阶:
//
//
// 你可以不使用代码库中的排序函数来解决这道题吗?
// 你能想出一个仅使用常数空间的一趟扫描算法吗?
//
// Related Topics 数组 双指针 排序 1132 0
package leetcode.editor.cn;
// Java:颜色分类
public class SortColors {
public static void main(String[] args) {
Solution solution = new SortColors().new Solution();
// TO TEST
int[] nums = new int[] {2, 0, 2, 1, 1, 0};
solution.sortColors(nums);
for (int num : nums) {
System.out.print(num + " ");
}
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
// 使用常数计算0 1 2的个数,然后对数组进行赋值
public void sortColors(int[] nums) {
int ZeroCount = 0, OneCount = 0, TwoCount = 0;
for (int num : nums) {
if (num == 0) {
ZeroCount++;
} else if (num == 1) {
OneCount++;
} else if (num == 2) {
TwoCount++;
}
}
for (int i = 0; i < nums.length; i++) {
if (i < ZeroCount) {
nums[i] = 0;
} else if (i < ZeroCount + OneCount) {
nums[i] = 1;
} else if (i < ZeroCount + OneCount + TwoCount) {
nums[i] = 2;
}
}
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
class Solution {
/**
* 递归方法,dfs
* 左边和右边更深的
* @param root
* @return
*/
public int maxDepth1(TreeNode root) {
if(root==null) return 0;
int leftDepth=0,rightDepth=0;
if(root.left!=null){
leftDepth=maxDepth1(root.left);
}
if(root.right!=null){
rightDepth=maxDepth1(root.right);
}
return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
}
/**
* bfs
* 一层一层进行拓展
* @param root
* @return
*/
public int maxDepth(TreeNode root){
if(root==null) return 0;
LinkedList<TreeNode> que=new LinkedList<>();
que.offer(root);
int ans=0;
while(!que.isEmpty()){
int size=que.size();
while(size>0){
TreeNode tmp=que.poll();
if(tmp.left!=null){
que.offer(tmp.left);
}
if(tmp.right!=null){
que.offer(tmp.right);
}
size--;
}
ans++;
}
return ans;
}
}
//Java:二叉搜索树的后序遍历序列
public class ErChaSouSuoShuDeHouXuBianLiXuLieLcof{
public static void main(String[] args) {
Solution solution = new ErChaSouSuoShuDeHouXuBianLiXuLieLcof().new Solution();
// TO TEST
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
/**
* 后序遍历:左右中
* 二叉搜索树:左 < 中,右 > 中
* 递归分治
* 终止条件:i>=j,此子树节点数量<=1
* 递推:
* 1.划分左右子树,遍历数组元素,找到第一个大于根节点的节点[i,m-1][m,j-1]
* 2.判断是否是二叉搜索树
* 左子树区间所有节点小于根节点
* 右子树区间所有节点大于根节点
* 如果满足这两个条件可判断是否是二叉搜索树
* 返回值,判断此树是否正确,此树的左子树是否正确,此树的右子树是否正确
*
* @param postorder
* @return
*/
public boolean verifyPostorder(int[] postorder) {
return recur(postorder,0,postorder.length-1);
}
public boolean recur(int[] postorder, int i, int j) {
if(i>=j) return true;
int p=i;
// for (int k = 0; k < j; k++) {
// if(postorder[k]>postorder[j]){
// m=k;
// }
// }
while(postorder[p]<postorder[j]) p++;
int m=p;
while(postorder[p]>postorder[j]) p++;
return p==j&&recur(postorder,i,m-1)&&recur(postorder,m,j-1);
}
}
/**
* BFS层次遍历
* 栈或者队列都是可以的
* 传进队列的顺序为右左
* 再构造二叉树
* @param root
* @return
*/
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
LinkedList<TreeNode> que=new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
TreeNode node=que.poll();
if(node.right!=null){
que.offer(node.right);
}
if(node.left!=null){
que.offer(node.left);
}
TreeNode tmp=node.right;
node.right=node.left;
node.left=tmp;
}
return root;
}
// n n
/**
* 递归
* 递归的交换左右节点,在root==null时退出
* @param root
* @return
*/
public TreeNode mirrorTree1(TreeNode root){
if(root==null) return null;
TreeNode node=mirrorTree(root.left);
root.left=mirrorTree(root.right);
root.right=node;
return root;
}
/**
* 递归方法,判断是否是对称的二叉树
* dfs:深度优先遍历
* left.left=right.right
* left.right=right.left
* 跳出循环:left=null,right==null
* 特殊情况:root==null
* @param root
* @return
*/
public boolean isSymmetric1(TreeNode root) {
// 没有考虑特殊情况
if(root==null) return true;
return isS(root.left,root.right);
}
public boolean isS(TreeNode left,TreeNode right){
// 三种情况
if(left==null&&right==null){
return true;
}else if(left==null||right==null||left.val!=right.val){
return false;
}else{
return isS(left.left,right.right)&&isS(left.right,right.left);
}
}
// n遍历n个节点 n和树的深度有关,最坏情况下树变成一个链表结构
/**
*广度优先遍历
* 定义两个队列
* 首先从队列中取出两个节点进行比较
* 将left的left节点和right的right节点放入队列
* 将left的right节点和right的left节点放入队列
* @param root
* @return
*/
public boolean isSymmetric(TreeNode root){
if(root==null) return true;
if(root.left==null&&root.right==null){
return true;}
LinkedList<TreeNode> que=new LinkedList<TreeNode>();
que.offer(root.left);
que.offer(root.right);
while(!que.isEmpty()){
TreeNode left=que.poll();
TreeNode right=que.poll();
if(left==null&&right==null) continue;
if(left==null||right==null||left.val!=right.val){
return false;
}
que.add(left.left);
que.add(right.right);
que.add(left.right);
que.add(right.left);
}
return true;
}
//n n
}
class Solution {
/**
* BFS
* 一个节点与另一个节点都为同一个节点的公共节点
* 一个节点为一个节点的父节点
* 一个节点与另一个节点的最近父节点不相同 ,需要网上进行比较
* 左右中
* (0 (3 5 *4) *2) (7 9 *8) *6
* 0 *2 3 *4 5 *6 7 *8 9
* *6 *2 0 *4 3 5 *8 7 9
* 找出两个节点的祖先
* 迭代
* p,q比root值小,在root左侧
* 比root值大,在root右侧
* 一个大一个小,返回root
* @param root
* @param p
* @param q
* @return
*/
public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
LinkedList<TreeNode> que=new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
TreeNode tmp=que.poll();
if(tmp.val>p.val&&tmp.val>q.val){
que.offer(tmp.left);
}
else if(tmp.val<p.val&&tmp.val<q.val){
que.offer(tmp.right);
}else{
return tmp;
}
}
return null;
}
// n 1
/**
* dfs
* p,q在root的右子树中递归root.right
* p,q在root的左子树中递归root.left
* 返回root
* @param root
* @param p
* @param q
* @return
*/
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
if(root.val<p.val&&root.val<q.val){
//递归并返回
return lowestCommonAncestor(root.right,p,q);
}
if(root.val>p.val&&root.val>q.val){
return lowestCommonAncestor(root.left,p,q);
}
return root;
}
// n n
}```
/**
* 找到p,q结点的祖先,比较得到最近的祖先
* 通过递归对二叉树进行后序遍历,当遇到结点p或q时返回。
* 从底至顶回溯,当结点p,q在结点root的异侧时,结点root为最近公共祖先,向上返回root
* 返回值:
* 1.当left和right同时为空,root的左右子树中都不包含p,q,返回null
* 2.当left和right同时不为空,p,q分列在root的异侧,因此root为最近公共祖先,返回root
* 3.当left为空,right不为空;p,q都不在root的左子树中,直接返回right,
* 1.p,q其中一个在root的右子树中,此时right指向p
* 2.p,q两节点都在root的右子树中,此时的right指向最近公共祖先结点
* 4.当left不为空,right为空,与情况3同理
* @param root
* @param p
* @param q
* @return
*/
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null||root==p||root==q) return root;//树为空时直接返回null,p和q中有等于root的,它们的最近公共祖先即为root
TreeNode left=lowestCommonAncestor(root.left,p,q);//递归左子节点,返回值记为 leftleft ;
TreeNode right = lowestCommonAncestor(root.right,p,q);//递归右子节点,返回值记为 rightleft ;
if(left==null&&right==null) return null;//当left和right同时为空,root的左右子树中都不包含p,q,返回null
if(left==null) return right;//left为空,right不为空;p,q都不在root的左子树中,直接返回right,
if(right==null) return left;//right为空,left不为空;p,q都不在root的右子树中,直接返回left,
return root;// 当left和right同时不为空,p,q分列在root的异侧,因此root为最近公共祖先,返回root
}
/**
* bfs
* @param root
* @return
*/
public int[] levelOrder(TreeNode root) {
if(root==null) return new int[0];
LinkedList<TreeNode> que=new LinkedList<>();
//add操作,访问元素,LinkedList或ArrayList都可以使用
ArrayList<Integer> list=new ArrayList<>();
que.add(root);
while(!que.isEmpty()){
TreeNode tmp=que.poll();
list.add(tmp.val);
if(tmp.left!=null){
que.add(tmp.left);
}
if(tmp.right!=null){
que.add(tmp.right);
}
}
int[] num=new int[list.size()];
for (int i = 0; i < list.size(); i++) {
num[i]= list.get(i);
}
return num;
}
/**
* 二叉树的层次遍历
* 一个队列用来做正常遍历
* 一个链表用来存储二叉树每一层的节点
* 每一层遍历完之后,将该层的链表加入双层链表中
* 核心要点:每一层遍历时,循环的开始为链表的长度,避免因为链表做了改变之后长度变化
* @param root
* @return
*/
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> que=new LinkedList<>();
//add操作,访问元素,LinkedList或ArrayList都可以使用
List<List<Integer>> list=new ArrayList<>();
if(root != null) que.add(root);
while(!que.isEmpty()){
//定义一个每一层节点的列表
List<Integer> tmp=new ArrayList<>();
//避免队列大小的改变
for (int i = que.size(); i > 0 ; i--) {
TreeNode node=que.poll();
tmp.add(node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
list.add(tmp);
}
return list;
}
}
/**
* 第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印
* 不对:每一层遍历,加入队列时改变顺序,或者遍历时改变顺序
* 利用双端队列的两端都可以添加元素的特性,设打印列表(双端队列)tmp,规定:奇数层添加至tmp尾部,偶数层添加至tmp头部
* @param root
* @return
*/
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> que=new LinkedList<>();
//add操作,访问元素,LinkedList或ArrayList都可以使用
List<List<Integer>> list=new ArrayList<>();
if(root != null) que.add(root);
while(!que.isEmpty()){
//定义一个每一层节点的列表
LinkedList<Integer> tmp=new LinkedList<>();
//避免队列大小的改变
for (int i = que.size(); i > 0 ; i--) {
TreeNode node=que.poll();
//二叉树为偶数层或奇数层
if(list.size()%2 == 0) tmp.addLast(node.val);
else tmp.addFirst(node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
list.add(tmp);
}
return list;
}
// 按之字形顺序打印二叉树
// 定义两个队列,一个队列存储所有,另一个存储每一层的节点
public static ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
if (pRoot == null) {
return arrayLists;
}
Queue<TreeNode> tmp = new LinkedList<>();
tmp.offer(pRoot);
while (!tmp.isEmpty()) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = tmp.size(); i > 0; i--) {
TreeNode node = tmp.poll();
if (node.left != null) {
tmp.add(node.left);
}
if (node.right != null) {
tmp.add(node.right);
}
if (arrayLists.size() % 2 == 0) {
// 偶数层,从左到右
list.add(list.size(), node.val);
} else {
// 奇数层,从右到左
list.add(0, node.val);
}
}
if (!list.isEmpty()) {
arrayLists.add(list);
}
}
return arrayLists;
}
//给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
//
//
//
// 示例 1:
//
//
//输入:root = [1,null,2,3]
//输出:[1,2,3]
//
//
// 示例 2:
//
//
//输入:root = []
//输出:[]
//
//
// 示例 3:
//
//
//输入:root = [1]
//输出:[1]
//
//
// 示例 4:
//
//
//输入:root = [1,2]
//输出:[1,2]
//
//
// 示例 5:
//
//
//输入:root = [1,null,2]
//输出:[1,2]
//
//
//
//
// 提示:
//
//
// 树中节点数目在范围 [0, 100] 内
// -100 <= Node.val <= 100
//
//
//
//
// 进阶:递归算法很简单,你可以通过迭代算法完成吗?
// Related Topics 栈 树 深度优先搜索 二叉树
// 621 0
package leetcode.editor.cn;
import com.sun.source.tree.Tree;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
//Java:二叉树的前序遍历
public class BinaryTreePreorderTraversal{
public static void main(String[] args) {
Solution solution = new BinaryTreePreorderTraversal().new Solution();
// TO TEST
}
//leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
// 递归算法,迭代算法
public List<Integer> preorderTraversal1(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
pre(root,res);
return res;
}
public void pre(TreeNode root,List<Integer> res){
if(root == null){
return;
}
res.add(root.val);
pre(root.left,res);
pre(root.right,res);
}
//在递归的时候隐式的维护了一个栈,我们在迭代的时候需要显式的将这个栈模拟出来
public List<Integer> preorderTraversal(TreeNode root){
List<Integer> res = new ArrayList<Integer>();
if(root == null){
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode node = root;
while(!stack.isEmpty() || node != null){
while(node != null){
res.add(node.val);
stack.push(node);
node = node.left;
}
node = stack.pop();
node = node.right;
}
return res;
}
}
//leetcode submit region end(Prohibit modification and deletion)
}
//输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
//
// B是A的子结构, 即 A中有出现和B相同的结构和节点值。
//
// 例如:
//给定的树 A:
//
// 3
// / \
// 4 5
// / \
// 1 2
//给定的树 B:
//
// 4
// /
// 1
//返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
//
// 示例 1:
//
// 输入:A = [1,2,3], B = [3,1]
//输出:false
//
//
// 示例 2:
//
// 输入:A = [3,4,5,1,2], B = [4,1]
//输出:true
//
// 限制:
//
// 0 <= 节点个数 <= 10000
// Related Topics 树 深度优先搜索 二叉树
// 361 0
package leetcode.editor.cn;
import java.util.LinkedList;
//Java:树的子结构
public class ShuDeZiJieGouLcof{
public static void main(String[] args) {
Solution solution = new ShuDeZiJieGouLcof().new Solution();
// TO TEST
}
//leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
/**
* B是A的子结构, 即 A中有出现和B相同的结构和节点值。
* // 3
* // / \
* // 4 5
* // / \
* // 1 2
* //给定的树 B:
* //
* // 4
* // /
* // 1
* BFS
* @param A
* @param B
* @return
*/
public boolean isSubStructure1(TreeNode A, TreeNode B) {
if(B == null) return false;
else if(A == null) return false;
LinkedList<TreeNode> list = new LinkedList<>();
list.add(A);
while(!list.isEmpty()){
TreeNode tmp = list.poll();
if(tmp.val==B.val){
if(isEqualAB(tmp,B)){
return true;
}
}
if(tmp.left!=null){
list.add(tmp.left);
}
if(tmp.right != null){
list.add(tmp.right);
}
}
return false;
}
private boolean isEqualAB(TreeNode A, TreeNode B) {
LinkedList<TreeNode> list = new LinkedList<>();
LinkedList<TreeNode> listB = new LinkedList<>();
list.add(A);
listB.add(B);
while(!list.isEmpty()){
TreeNode tmpA = list.poll();
TreeNode tmpB = listB.poll();
if(tmpA.val!=tmpB.val){
return false;
}
if(tmpA.left!=null){
if(tmpB.left!=null){
list.add(tmpA.left);
listB.add(tmpB.left);
}else{
return false;
}
}
if(tmpA.right != null){
if(tmpB.right != null){
list.add(tmpA.right);
listB.add(tmpB.right);
}else{
return false;
}
}
}
return true;
}
/**
* 先序遍历树A的每个节点
* 判断树A中是否包含树B recur(A,B)
* recur(A,B)
* 终止条件:
* 当节点B为空,树B已经匹配完成,返回true
* 当节点A为空,说明已经越过树A节点,匹配失败,返回false
* 当节点A和B的值不同,匹配失败,返回false
* 返回值:
* 1.判断A和B的左子节点是否相等 recur(A.left,B.left)
* 2.判断A和B的右子节点是否相等,recur(A.right,B.right)
* isSubStructure(A,B)
* 1.当A或者B为空的时候 返回false
* 2.如果B是A的子结构,满足以下三种情况之一
* 1.以节点A为根节点的子树包含树B recur(A,B)
* 2.树B是树A左子树的子结构 isSubStructure(A.left,B)
* 3.树B是树A有子树的子结构 isSubStructure(A.right,B)
* @param A
* @param B
* @return
*/
public boolean isSubStructure(TreeNode A, TreeNode B){
if((A==null)||B==null) {
return false;
}
return recur(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B);
}
private boolean recur(TreeNode A, TreeNode B) {
if(B==null) return true;
if(A==null||A.val!=B.val) return false;
return recur(A.left,B.left)&&recur(A.right,B.right);
}
}
//leetcode submit region end(Prohibit modification and deletion)
}
//给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
//
// 叶子节点 是指没有子节点的节点。
//
//http://123.207.85.44:9001/acoms/commonFlow/queryTaskByUserId
//
// 示例 1:
//
//
//
//
//输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
//输出:[[5,4,11,2],[5,8,4,5]]
//
//
// 示例 2:
//
//
//
//
//输入:root = [1,2,3], targetSum = 5
//输出:[]
//
//
// 示例 3:
//
//
//输入:root = [1,2], targetSum = 0
//输出:[]
//
//
//
//
// 提示:
//
//
// 树中节点总数在范围 [0, 5000] 内
// -1000 <= Node.val <= 1000
// -1000 <= targetSum <= 1000
//
//
// 注意:本题与主站 113 题相同:https://leetcode-cn.com/problems/path-sum-ii/
// Related Topics 树 深度优先搜索 回溯 二叉树 262 0
package leetcode.editor.cn;
import java.util.LinkedList;
import java.util.List;
//Java:二叉树中和为某一值的路径
public class ErChaShuZhongHeWeiMouYiZhiDeLuJingLcof{
public static void main(String[] args) {
Solution solution = new ErChaShuZhongHeWeiMouYiZhiDeLuJingLcof().new Solution();
// TO TEST
}
//leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
/**
* @description: 深度优先搜索,回溯,先序遍历+路径记录
* 记录从根节点到当前节点的路径,当各节点值的和等于目标值sum时,将路径加入结果列表
*
* @param: root
* @param: target
* @return: java.util.List>
* @author:
* @create: 2021-11-10
*/
// 初始化:结果列表res,路径列表path
// 返回值:返回res
List<List<Integer>> list = new LinkedList<List<Integer>>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
LinkedList<Integer> path = new LinkedList<>();
recur(root,path,target);
return list;
}
public void recur(TreeNode root,LinkedList<Integer> path,int target){
if(root == null){
return;
}
// 1.路径更新
path.add(root.val);
// 2.目标值更新
target -= root.val;
// 3.路径记录
// 当root为叶节点且路径和等于目标值
if(root.left == null && root.right == null && target == 0){
// 将path对象加入了list,后续path改变时list中的path对象也会改变
// list.add(path);
// 复制一个path并且加入到list
list.add(new LinkedList<>(path));
// 有回溯操作,不需要将path置为空
// while(!path.isEmpty()){
// path.remove();
// }
}
// 4.先序遍历,递归左右子节点
recur(root.left,path,target);
recur(root.right,path,target);
// 5.路径恢复,向上回溯前,需要将当前节点从路径path中删除
path.removeLast();
}
}
//leetcode submit region end(Prohibit modification and deletion)
}
//输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
//
//
//
// 为了让您更好地理解问题,以下面的二叉搜索树为例:
//
//
//
//
//
//
//
// 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是
//第一个节点。
//
// 下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
//
//
//
//
//
//
//
// 特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
//
//
//
// 注意:本题与主站 426 题相同:https://leetcode-cn.com/problems/convert-binary-search-tree-
//to-sorted-doubly-linked-list/
//
// 注意:此题对比原题有改动。
// Related Topics 栈 树 深度优先搜索 二叉搜索树 链表 二叉树 双向链表 348 0
package leetcode.editor.cn;
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
//Java:二叉搜索树与双向链表
public class ErChaSouSuoShuYuShuangXiangLianBiaoLcof{
public static void main(String[] args) {
Solution solution = new ErChaSouSuoShuYuShuangXiangLianBiaoLcof().new Solution();
// TO TEST
}
//leetcode submit region begin(Prohibit modification and deletion)
// Definition for a Node.
class Solution {
/**
* @description: 中序遍历,左中右,当转化完成后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继,还需要返回链表中的第一个节点的指针
* 将二叉搜索树转换成一个排序的循环双向链表
* 1.排序链表:节点从小到大排序,使用中序遍历
* 2.双向链表:在构建相邻节点的引用关系时,设置前驱节点pre和当前节点cur,构建pre.right = cur,cur.left = pre
* 3.循环链表:设链表头节点head和尾结点tail,head.left=tail和tail.right = head
* 使用中序遍历访问树的各节点cur,在访问每个节点时构建cur和前驱节点pre的引用指向,中序遍历完成后,最后构建头节点和尾结点的引用指向
* @param: root
* @return: leetcode.editor.cn.Node
* @author:
* @create: 2021-11-10
*/
Node pre,head;
public Node treeToDoublyList(Node root) {
// 1.若节点root为空,直接返回
if(root == null) {
return null;
}
// 2.初始化:空节点pre
// 3.转化为双向链表:调用dfs(root)
dfs(root);
// 4.构建循环链表:中序遍历完成后,head指向头节点,pre指向尾结点,因此修改head和pre的双向节点引用
head.left = pre;
pre.right = head;
// 5.返回值:返回链表的头节点head
return head;
}
/**
* @description: 1.终止条件:当节点cur为空,代表越过叶节点,直接返回
* 2.递归左子树,dfs(cur.left)
* 3.构建链表:
* 1.当pre为空时:代表正在访问链表头节点,记为head
* 2.当pre不为空时:修改双向节点引用,pre.right = cur cur.left = pre
* 3.保存cur:更新pre = cur,即节点cur时后继节点的pre
* 4.递归右子树dfs(cur.right)
* @param: root
* @return: void
* @author:
* @create: 2021-11-11
*/
public void dfs(Node cur) {
if(cur == null){
return;
}
dfs(cur.left);
// 反转链表
if(pre == null){
head = cur;
}else{
pre.right = cur;
}
cur.left = pre;
pre = cur;
dfs(cur.right);
}
}
//leetcode submit region end(Prohibit modification and deletion)
}
// 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
//
//
//
// 示例 1:
//
//
// 输入:n = 3
// 输出:5
//
//
// 示例 2:
//
//
// 输入:n = 1
// 输出:1
//
//
//
//
// 提示:
//
//
// 1 <= n <= 19
//
// Related Topics 树 二叉搜索树 数学 动态规划 二叉树 1502 0
package leetcode.editor.cn;
// Java:不同的二叉搜索树
public class UniqueBinarySearchTrees {
public static void main(String[] args) {
Solution solution = new UniqueBinarySearchTrees().new Solution();
// TO TEST
/*int[] nums = {2, 3, 1, 4, 5};
solution.sort(nums, 0, nums.length - 1);
System.out.println(Arrays.toString(nums));*/
System.out.println(solution.numTrees(3));
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
/*
* 动态规划
* 遍历每个数字i作为树根,将(1,i-1)序列作为左子树,将(i+1,n)作为右子树,递归构建左子树和右子树
* G(n):长度为n的序列能构成的不同二叉搜索树的个数.G(0)=G(1)=1
* F(i,n):以i为根,序列长度为n的不同二叉搜索树个数=G(i-1,n-i).F(3,7)=G(2)*G(4)
*
*/
public int numTrees(int n) {
int[] G = new int[n + 1];
G[0] = 1;
G[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
G[i] += G[j - 1] * G[i - j];
}
}
return G[n];
}
public void sort(int[] nums, int i, int j) {
int key = nums[i];
int left = i, right = j;
if (left >= right) {
return;
}
while (left < right) {
while (left < right && key < nums[right]) {
right--;
}
if (right > 0) {
nums[left] = nums[right];
}
while (left < right && key > nums[left]) {
left++;
}
if (left > 0) {
nums[right] = nums[left];
}
}
nums[left] = key;
sort(nums, i, left - 1);
sort(nums, left + 1, j);
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
// 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
//
// 有效 二叉搜索树定义如下:
//
//
// 节点的左子树只包含 小于 当前节点的数。
// 节点的右子树只包含 大于 当前节点的数。
// 所有左子树和右子树自身必须也是二叉搜索树。
//
//
//
//
// 示例 1:
//
//
// 输入:root = [2,1,3]
// 输出:true
//
//
// 示例 2:
//
//
// 输入:root = [5,1,4,null,null,3,6]
// 输出:false
// 解释:根节点的值是 5 ,但是右子节点的值是 4 。
//
//
//
//
// 提示:
//
//
// 树中节点数目范围在[1, 10⁴] 内
// -2³¹ <= Node.val <= 2³¹ - 1
//
// Related Topics 树 深度优先搜索 二叉搜索树 二叉树 1387 0
package leetcode.editor.cn;
// Java:验证二叉搜索树
public class ValidateBinarySearchTree {
public static void main(String[] args) {
Solution solution = new ValidateBinarySearchTree().new Solution();
// TO TEST
TreeNode root = new TreeNode(2);
TreeNode rootLeft = new TreeNode(1);
TreeNode rootRight = new TreeNode(3);
root.left = rootLeft;
root.right = rootRight;
System.out.println(solution.isValidBST(root));
}
// leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for a binary tree node. public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() {}
* TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val;
* this.left = left; this.right = right; } }
*/
class Solution {
// 节点的左子树只包含 小于 当前节点的数。
// 节点的右子树只包含 大于 当前节点的数。
// 所有左子树和右子树自身必须也是二叉搜索树。
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode root, long lower, long higher) {
if (root == null) {
return true;
}
if (root.val <= lower || root.val >= higher) {
return false;
}
return isValidBST(root.left, lower, root.val) && isValidBST(root.right, root.val, higher);
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
// 给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。
//
//
//
// 示例 1:
//
//
// Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
// Output: [3,9,20,null,null,15,7]
//
//
// 示例 2:
//
//
// Input: preorder = [-1], inorder = [-1]
// Output: [-1]
//
//
//
//
// 提示:
//
//
// 1 <= preorder.length <= 3000
// inorder.length == preorder.length
// -3000 <= preorder[i], inorder[i] <= 3000
// preorder 和 inorder 均无重复元素
// inorder 均出现在 preorder
// preorder 保证为二叉树的前序遍历序列
// inorder 保证为二叉树的中序遍历序列
//
// Related Topics 树 数组 哈希表 分治 二叉树 1385 0
package leetcode.editor.cn;
import java.util.HashMap;
// Java:从前序与中序遍历序列构造二叉树
public class ConstructBinaryTreeFromPreorderAndInorderTraversal {
public static void main(String[] args) {
Solution solution = new ConstructBinaryTreeFromPreorderAndInorderTraversal().new Solution();
// TO TEST
solution.buildTree(new int[] {3, 9, 20, 15, 7}, new int[] {9, 3, 15, 20, 7});
}
// leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for a binary tree node. public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() {}
* TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val;
* this.left = left; this.right = right; } }
*/
class Solution {
// Input: preorder = [3,9,20,15,7], 中左右
// inorder = [9,3,15,20,7] 左中右
// Output: [3,9,20,null,null,15,7]
// 构造二叉树,返回根节点,哈希表,分治
// 将中序遍历每个元素的位置存入哈希表中,通过前序遍历的元素的值
// 就能获取到中序遍历中该元素的位置,从而划分左右的元素,构建做二叉树和右二叉树
HashMap<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return helper(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
private TreeNode helper(int[] preorder, int p_start, int p_end, int[] inorder, int i_start, int i_end) {
if (p_start == p_end) {
return null;
}
int e = preorder[p_start];
TreeNode root = new TreeNode(e);
int index = map.get(e);
int leftLength = index - i_start;
root.left = helper(preorder, p_start + 1, p_start + leftLength + 1, inorder, i_start, index);
root.right = helper(preorder, p_start + leftLength + 1, p_end, inorder, index + 1, i_end);
return root;
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
// 给你二叉树的根结点 root ,请你将它展开为一个单链表:
//
//
// 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
// 展开后的单链表应该与二叉树 先序遍历 顺序相同。
//
//
//
//
// 示例 1:
//
//
// 输入:root = [1,2,5,3,4,null,6]
// 输出:[1,null,2,null,3,null,4,null,5,null,6]
//
//
// 示例 2:
//
//
// 输入:root = []
// 输出:[]
//
//
// 示例 3:
//
//
// 输入:root = [0]
// 输出:[0]
//
//
//
//
// 提示:
//
//
// 树中结点数在范围 [0, 2000] 内
// -100 <= Node.val <= 100
//
//
//
//
// 进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?
// Related Topics 栈 树 深度优先搜索 链表 二叉树 1042 0
package leetcode.editor.cn;
// Java:二叉树展开为链表
public class FlattenBinaryTreeToLinkedList {
public static void main(String[] args) {
Solution solution = new FlattenBinaryTreeToLinkedList().new Solution();
// TO TEST
TreeNode root = new TreeNode(1);
TreeNode rootLeft = new TreeNode(2);
TreeNode rootLeftLeft = new TreeNode(3);
TreeNode rootLeftRight = new TreeNode(4);
TreeNode rootRight = new TreeNode(5);
TreeNode rootRightRight = new TreeNode(6);
root.left = rootLeft;
rootLeft.left = rootLeftLeft;
rootLeft.right = rootLeftRight;
root.right = rootRight;
rootRight.right = rootRightRight;
solution.flatten(root);
while (root != null) {
System.out.println(root.val);
root = root.right;
}
}
// leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for a binary tree node. public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() {}
* TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val;
* this.left = left; this.right = right; } }
*/
class Solution {
// 栈 树 深度优先搜索 链表 二叉树
// 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
// 展开后的单链表应该与二叉树 先序遍历 顺序相同。中左右
// 类似后序遍历,利用递归 右左中
// 保存
TreeNode pre = null;
public void flatten(TreeNode root) {
if (root == null) {
return;
}
flatten(root.right);
flatten(root.left);
root.left = null;
root.right = pre;
pre = root;
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
//统计一个数字在排序数组中出现的次数。
//
//
//
// 示例 1:
//
// 输入: nums = [5,7,7,8,8,10], target = 8
//输出: 2
//
// 示例 2:
//
// 输入: nums = [5,7,7,8,8,10], target = 6
//输出: 0
//
//
//
// 限制:
//
// 0 <= 数组长度 <= 50000
//
//
//
// 注意:本题与主站 34 题相同(仅返回值不同):https://leetcode-cn.com/problems/find-first-and-last-
//position-of-element-in-sorted-array/
// Related Topics 数组 二分查找
// 139 0
package leetcode.editor.cn;
//Java:在排序数组中查找数字 I
public class ZaiPaiXuShuZuZhongChaZhaoShuZiLcof{
public static void main(String[] args) {
Solution solution = new ZaiPaiXuShuZuZhongChaZhaoShuZiLcof().new Solution();
// TO TEST
int[] nums={5,7,7,8,8,10};
System.out.println(solution.search(nums,8));
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
/**
* 二分查找
* 比中间小就在左边查找,比中间大就在右边查找
* @param nums
* @param target
* @return
*/
public int search1(int[] nums, int target) {
int count=0;
for (int i = 0; i < nums.length; i++) {
if(nums[i]==target){
count++;
}
}
return count;
// 二分查找
// int left=0,right=nums.length;
// int mid=(left+right)/2;
// while(nums[mid]!=target){
// if(nums[mid]>target){
// right=mid--;
// }
// if(nums[mid]
// left=mid++;
// }
// mid=(left+right)/2;
// }
// return mid;
}
/**
* 使用二分法分别找到左边界和右边界right-left-1
*/
public int search(int[] nums, int target){
//搜索右边界right
int i=0,j=nums.length-1;
while(i<=j){
int m=(i+j)/2;
if(nums[m]<=target){
i=m+1;
}else{
j=m-1;
}
}
int right=i;
//若数组没有target,提前返回
if(j>=0&&nums[j]!=target) return 0;
//搜索左边界right
i=0;
j=nums.length-1;
while(i<=j){
int m=(i+j)/2;
if(nums[m]<target) i=m+1;
else j=m-1;
}
int left=j;
return right-left-1;
}
//优化,将二分查找封装,分别二分查找target和target-1的右边界,将两结果相减并返回
}
//leetcode submit region end(Prohibit modification and deletion)
}
// 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
//
// 如果数组中不存在目标值 target,返回 [-1, -1]。
//
// 进阶:
//
//
// 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
//
//
//
//
// 示例 1:
//
//
// 输入:nums = [5,7,7,8,8,10], target = 8
// 输出:[3,4]
//
// 示例 2:
//
//
// 输入:nums = [5,7,7,8,8,10], target = 6
// 输出:[-1,-1]
//
// 示例 3:
//
//
// 输入:nums = [], target = 0
// 输出:[-1,-1]
//
//
//
// 提示:
//
//
// 0 <= nums.length <= 10⁵
// -10⁹ <= nums[i] <= 10⁹
// nums 是一个非递减数组
// -10⁹ <= target <= 10⁹
//
// Related Topics 数组 二分查找 1402 0
package leetcode.editor.cn;
// Java:在排序数组中查找元素的第一个和最后一个位置
public class FindFirstAndLastPositionOfElementInSortedArray {
public static void main(String[] args) {
Solution solution = new FindFirstAndLastPositionOfElementInSortedArray().new Solution();
// TO TEST
int[] res = solution.searchRange(new int[] {1}, 0);
for (int re : res) {
System.out.print(re + " ");
}
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
// 二分查找 O(log n)
// 在排序数组中查找元素的第一个和最后一个位置
// 分别查找到左边界和右边界
public int[] searchRange(int[] nums, int target) {
int[] res = new int[2];
int left = 0, right = nums.length - 1;
// 右边界
right = binarySearch(left, right, nums, target);
// 如果数组没有target,直接返回
if (nums.length == 0 || right == -1 || nums[right] != target) {
res[0] = -1;
res[1] = -1;
return res;
}
res[1] = right;
left = 0;
right = nums.length - 1;
// 左边界
right = binarySearch(left, right, nums, target - 1);
res[0] = right + 1;
return res;
}
public int binarySearch(int left, int right, int[] nums, int target) {
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
//给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
//
// 请必须使用时间复杂度为 O(log n) 的算法。
//
//
//
// 示例 1:
//
//
//输入: nums = [1,3,5,6], target = 5
//输出: 2
//
//
// 示例 2:
//
//
//输入: nums = [1,3,5,6], target = 2
//输出: 1
//
//
// 示例 3:
//
//
//输入: nums = [1,3,5,6], target = 7
//输出: 4
//
//
// 示例 4:
//
//
//输入: nums = [1,3,5,6], target = 0
//输出: 0
//
//
// 示例 5:
//
//
//输入: nums = [1], target = 0
//输出: 0
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 104
// -104 <= nums[i] <= 104
// nums 为无重复元素的升序排列数组
// -104 <= target <= 104
//
// Related Topics 数组 二分查找
// 1005 0
package leetcode.editor.cn;
//Java:搜索插入位置
public class SearchInsertPosition{
public static void main(String[] args) {
Solution solution = new SearchInsertPosition().new Solution();
// TO TEST
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int searchInsert(int[] nums, int target) {
// 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
//使用二分查找
int left=0,right=nums.length-1;
int mid;
while(left<=right){
mid=(right+left)/2;
if(nums[mid]==target){
return mid;
}
if(nums[mid]>target) right=mid-1;
else left=mid+1;
}
return left;
}
}
//leetcode submit region end(Prohibit modification and deletion)
}
// 整数数组 nums 按升序排列,数组中的值 互不相同 。
//
// 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[
// k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2
// ,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
//
// 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
//
//
//
// 示例 1:
//
//
// 输入:nums = [4,5,6,7,0,1,2], target = 0
// 输出:4
//
//
// 示例 2:
//
//
// 输入:nums = [4,5,6,7,0,1,2], target = 3
// 输出:-1
//
// 示例 3:
//
//
// 输入:nums = [1], target = 0
// 输出:-1
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 5000
// -10^4 <= nums[i] <= 10^4
// nums 中的每个值都 独一无二
// 题目数据保证 nums 在预先未知的某个下标上进行了旋转
// -10^4 <= target <= 10^4
//
//
//
//
// 进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
// Related Topics 数组 二分查找 1781 0
package leetcode.editor.cn;
// Java:搜索旋转排序数组
public class SearchInRotatedSortedArray {
public static void main(String[] args) {
Solution solution = new SearchInRotatedSortedArray().new Solution();
// TO TEST
int[] nums = new int[] {4, 5, 6, 7, 0, 1, 2};
System.out.println(solution.search(nums, 3));
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
//
// [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
// 输入:nums = [4,5,6,7,0,1,2], target = 0
// 输出:4
// 如果[l,mid-1]是有序数组,target的大小在nums[l]-nums[mid]中,应该将搜索范围缩小至l,mid-1,否则在mid+1,r中查找
// 如果[mid+1,r]是有序数组,target的大小在nums[mid+1]-nums[r]中,应该将搜索范围缩小至mid+1,r,否则在l,mid-1中查找
public int search(int[] nums, int target) {
if (nums.length == 0) {
return -1;
}
if (nums.length == 1) {
return target == nums[0] ? 0 : -1;
}
int len = nums.length - 1;
int l = 0, r = len;
while (l >= 0 && l <= r) {
int mid = (l + r) / 2;
if (target == nums[mid]) {
return mid;
}
if (nums[l] <= nums[mid]) {
if (target >= nums[l] && target <= nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (target >= nums[mid + 1] && target <= nums[r]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
}
// leetcode submit region end(Prohibit modification and deletion)
}
/*最长上升子序列(三) 给定数组 arr ,设长度为 n ,输出 arr 的最长上升子序列。(如果有多个答案,请输出其中 按数值(注:区别于按单个字符的ASCII码值)进行比较的 字典序最小的那个)
输入:
[1,2,8,6,4]
复制
返回值:
[1,2,4]
复制
说明:
其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个 按数值进行比较的字典序 最小,故答案为(1,2,4)*/
// 1 2 3 3 3
// 动态规划
// 维护一个单调栈,然后进行比较
// 使用二分法进行优化
/*
* 状态定义:dp[i]代表nums以nums[i]结尾的最长子序列长度
* 转移方程:dp[i] = max(dp[i],dp[j]+1) for j in [0,i)
* 初始状态:dp[i]所有元素都置为1.此时长度都为1
* 返回值:返回dp列表的最长子序列
*/
/*
* 状态定义:维护一个列表tails,每个元素的值代表长度为k+1的子序列尾部元素的值
* 转移方程: res为tails的长度,代表当前的最长上升子序列长度,每轮遍历nums[k]时,通过二分法遍历[0,res)列表区间,找出nums[k]的大小分界点
* 1.区间中存在tails[i]>nums[k]:将第一个满足tails[i]>nums[k]执行tails[i] = nums[k],更小的nums[k]后面更可能接一个比它大的数字
* 2.区间中不存在tails[i]>nums[k]:nums[k]可以接在所有长度的子序列的后面,接在最长的后面
* 初始状态:tails列表所有的值为0
* 返回tails列表
*/
public static int[] LIS(int[] arr) {
// write code here
int[] tails = new int[arr.length];
int[] res = new int[arr.length];
int len = 0;
Arrays.fill(tails, 0);
for (int i = 0; i < arr.length; i++) {
int mid = binarySearch(tails, arr[i], len);
tails[mid] = arr[i];
if (len == mid) {
len++;
res = Arrays.copyOfRange(tails, 0, len);
}
}
return res;
}
// 二分查找
private static int binarySearch(int[] tails, int k, int len) {
int left = 0, right = len;
while (left < right) {
int mid = (left + right) / 2;
if (tails[mid] < k) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
// 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
//
//
// 每行的元素从左到右升序排列。
// 每列的元素从上到下升序排列。
//
//
//
//
// 示例 1:
//
//
// 输入:matrix = [[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 = 5
// 输出:true
//
//
// 示例 2:
//
//
// 输入:matrix = [[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 = 20
// 输出:false
//
//
//
//
// 提示:
//
//
// m == matrix.length
// n == matrix[i].length
// 1 <= n, m <= 300
// -10⁹ <= matrix[i][j] <= 10⁹
// 每行的所有元素从左到右升序排列
// 每列的所有元素从上到下升序排列
// -10⁹ <= target <= 10⁹
//
// Related Topics 数组 二分查找 分治 矩阵 915 0
package leetcode.editor.cn;
// Java:搜索二维矩阵 II
public class SearchA2dMatrixIi {
public static void main(String[] args) {
Solution solution = new SearchA2dMatrixIi().new Solution();
// TO TEST
System.out
.println(solution.searchMatrix(new int[][] {{1, 4, 7, 11, 15}, {2, 5, 8, 12, 19}, {3, 6, 9, 16, 22}}, 5));
}
// leetcode submit region begin(Prohibit modification and deletion)
class Solution {
// 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target
// 每行的元素从左到右升序排列。
// 每列的元素从上到下升序排列。
// 输入:matrix = [[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 = 5
// 输出:true
/*
* 二分查找 分治 矩阵
* // * 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
*/
public boolean searchMatrix1(int[][] matrix, int target) {
boolean flag;
for (int i = 0; i < matrix.length; i++) {
// 提前结束:当第一个元素大于target或者最后一个元素小于target
if (matrix[i][0] > target || matrix[i][matrix[0].length - 1] < target) {
continue;
}
if (binarySearch(matrix[i], target)) {
return true;
}
}
return false;
}
public boolean binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return true;
}
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
/*
* 从右上角的位置触发开始遍历,每次向左的数字会变小,向下的数字会变大
* 如果target的值大于当前值,向下走
* 如果target的值小于当前值,就向左走
*/
public boolean searchMatrix(int[][] matrix, int target) {
int i = 0, j = matrix[0].length - 1;
int cur;
while (true) {
if (i >= matrix.length || j < 0) {
return false;
}
cur = matrix[i][j];
if (cur == target) {
return true;
}
if (cur < target) {
i++;
} else {
j--;
}
}
}
}
// leetcode submit region end(Prohibit modification and deletion)
}