类型:数组+移位/排序
1.数组元素循环左移p位后的结果。
循环左移和以下操作等价:
1.通过中间元素对称翻转
2.将数组分为n-p个和p个两部分
3.分别翻转这两个数组即可

// 中心翻转
void Reverse(int R[], int left, int right) {
    while (left < right) {
        int temp = R[left];
        R[left] = R[right];
        R[right] = temp;
        left++;
        right--;
    }
}
//分组再翻转
void CyclicLeftShift(int R[], int n, int p) {
    Reverse(R, 0, n-1);       // 全部逆置
    Reverse(R, 0, p-1);       // 前p个逆置
    Reverse(R, p, n-1);       // 后n-p个逆置
}

2.两个整数递增有序序列A,B分别有n和m个元素,求第K大的数(1≤k≤n+m),要求算法有最佳时间复杂度
例子:输入A={1,3,4,5,6},B={3,4,5,6},K=4
思路:
A、B递增,从大到小数到第k个即可。(时间复杂度o(k))

public class KthLargestInTwoArrays {
    
    /**
     * 方法1:双指针合并 - 时间复杂度O(K),空间复杂度O(1)
     */
    public static int findKthLargest1(int[] A, int[] B, int k) {
        int n = A.length, m = B.length;
        int i = n - 1, j = m - 1; // 从后往前遍历(因为要求第K大)
        int count = 0;
        int result = 0;
        // 两个数组都还没数完的情况
        while (i >= 0 && j >= 0) {
            count++;
            if (A[i] >= B[j]) {
                if (count == k) return A[i];
                i--;
            } else {
                if (count == k) return B[j];
                j--;
            }
        }
        
        // 如果其中一个数组遍历完
        while (i >= 0) {
            count++;
            if (count == k) return A[i];
            i--;
        }
        
        while (j >= 0) {
            count++;
            if (count == k) return B[j];
            j--;
        }
    
        return -1; // k超出范围
    }
}

类型:数学
3. 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
思路:
相当于一个减法运算,得到最大差值。
所以找到最大的被减数和最小的减数即可。

public class StockProfit {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        //初始值-最低价格,遇到最低价格则更新
        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;
        
        for (int i = 0; i < prices.length; i++) {
            // 更新最低价格
            if (prices[i] < minPrice) {
                minPrice = prices[i];
            }else if (prices[i] - minPrice > maxProfit) {
            // 计算当前价格卖出能获得的利润,更新最大利润
                maxProfit = prices[i] - minPrice;
            }
        }
        return maxProfit;
    }
}

类型 :数组+双指针
4. 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
思路:

  • 相当于求出宽*高,也就是(b-a)*min(b,a)的最大值。乘数越大越好
  • 只需更换a,b的值就好,更换条件,比min(b,a)大则更换,否则不换
	public class Solution {
    public int maxArea(int[] height) {
        int left = 0;                    // 左指针
        int right = height.length - 1;   // 右指针
        int maxArea = 0;                 // 最大面积
        
        while (left < right) {
            // 计算当前容器的面积
            int currentWidth = right - left;
            int currentHeight = Math.min(height[left], height[right]);
            int currentArea = currentWidth * currentHeight;
            
            // 更新最大面积
            maxArea = Math.max(maxArea, currentArea);
            
            // 移动较短的指针,希望找到更高的垂线
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        
        return maxArea;
    }
}

类型:列表 + 堆 遍历
5. 有 k 个 非递减排列 的整数列表。找到一个最小 区间,使得 k 个列表中的每个列表至少有一个数包含在其中。
思路:
1.先随机取每个子列表的一个元素,存放在最小堆中,找出最大值和最小值组成区间
2.取出最小值,依次存入子列表的一个新值,更新最小区间

public int[] smallestRange(List<List<Integer>> nums) {
        // 最小堆,存储三元组[元素值, 列表索引, 元素在列表中的索引]
        // 堆按照元素值进行排序,最小的元素在堆顶
        PriorityQueue<int[]> minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]);

        int currentMax = Integer.MIN_VALUE; // 记录当前堆中所有元素的最大值
        int k = nums.size(); // 列表的数量

        // 初始化堆:将每个列表的第一个元素加入堆中
        for (int i = 0; i < k; i++) {
            // 检查当前列表是否为空
            if (!nums.get(i).isEmpty()) {
                int val = nums.get(i).get(0); // 获取当前列表的第一个元素
                // 将三元组[元素值, 列表索引, 元素索引]加入堆
                minHeap.offer(new int[]{val, i, 0});
                // 更新当前最大值
                currentMax = Math.max(currentMax, val);
            }
        }

        // 初始化最小区间的起点和终点,设置为一个非常大的范围
        int start = -1000000, end = 1000000;
        int minRange = end - start; // 初始化最小区间长度

        // 当堆中有k个元素时继续处理(确保每个列表至少有一个元素在考虑范围内)
        while (minHeap.size() == k) {
            // 从堆中取出当前最小的元素
            int[] current = minHeap.poll();
            int currentVal = current[0];   // 最小元素的值
            int listIdx = current[1];      // 当前元素所在的列表索引
            int elementIdx = current[2];   // 当前元素在列表中的索引

            // 检查当前区间[currentVal, currentMax]是否比之前记录的最小区间更小
            // 或者区间长度相同但起点更小(题目要求返回最小的区间)
            if (currentMax - currentVal < minRange ||
                    (currentMax - currentVal == minRange && currentVal < start)) {
                // 更新最小区间信息
                minRange = currentMax - currentVal;
                start = currentVal;
                end = currentMax;
            }

            // 如果当前元素所在的列表还有下一个元素
            if (elementIdx + 1 < nums.get(listIdx).size()) {
                // 获取下一个元素的值
                int nextVal = nums.get(listIdx).get(elementIdx + 1);
                // 将下一个元素加入堆中
                minHeap.offer(new int[]{nextVal, listIdx, elementIdx + 1});
                // 更新当前最大值(因为新加入的元素可能比当前最大值更大)
                currentMax = Math.max(currentMax, nextVal);
            } else {
                // 如果当前列表已经遍历完,则退出循环
                // 因为无法保证每个列表至少有一个元素在区间内了
                break;
            }
        }

        // 返回找到的最小区间
        return new int[]{start, end};
    }
main(){
	Solution solution = new Solution();
        // 创建测试数据
        List<List<Integer>> nums = new ArrayList<>();
        nums.add(Arrays.asList(4, 10, 15, 24, 26));
        nums.add(Arrays.asList(0, 9, 12, 20));
        nums.add(Arrays.asList(5, 18, 22, 30));

        // 调用方法并输出结果
        int[] result = solution.smallestRange(nums);
}

DFS深度遍历
6.给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。假设该网格的四条边均被水包围。

思路:计算不连通无向图的个数

public class Island {
//    DFS
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
//        定义岛屿的数量/几行/几列
        int count = 0;
        int rows = grid.length;
        int cols = grid[0].length;

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                // 发现新的岛屿
                if (grid[i][j] == '1') {
                    count++;
                    dfs(grid, i, j);
                }
            }
        }
        return count;
    }

    private void dfs(char[][] grid, int i, int j) {
        int rows = grid.length;
        int cols = grid[0].length;
        // 边界检查
        if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] == '0') {
            return;
        }
        // 将当前陆地标记为已访问(改为水)
        grid[i][j] = '0';
        // 递归访问四个方向
        dfs(grid, i - 1, j); // 上
        dfs(grid, i + 1, j); // 下
        dfs(grid, i, j - 1); // 左
        dfs(grid, i, j + 1); // 右
    }

    public static void main(String[] args) {
        char[][] grid = {
                {'1', '1', '1', '0'},
                {'1', '1', '0', '1'},
                {'1', '1', '1', '1'},
                {'0', '1', '1', '0'}
        };
        System.out.println(new Island().numIslands(grid));
    }
}

遍历路径如下:先红色路径到底,再绿色路径到底
在这里插入图片描述
拓扑排序
7.你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [a, b] ,表示如果要学习课程 ai则 必须 先学习课程 b。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

思路:计算所有不连通有向图的节点总个数,用邻接表+拓扑图计算。
图结构:
顶点:课程(0到numCourses-1)
边:先修关系,如[ai, bi]表示从bi指向ai的有向边
邻接表:存储每个顶点的出边(指向的课程)

import java.util.ArrayList;
import java.util.*;

public class Course {
	// 空间上:1.存储课程依赖关系的邻接表 2.每门课程的入度  3.当前可以学习的课程队列
	// 关系:取出入度为0的数据,根据入度为0的依赖关系取出对应的课程,取出并计数,更新课程入度。循环
    public  boolean isFinish(int numCourses,int[][] prerequisites) {
//       1. 创建邻接表--存储课程之间的依赖关系
        List<List<Integer>> adjList = new ArrayList<>(numCourses);
        for (int i = 0; i < numCourses; i++) {
            adjList.add(new ArrayList<>());
        }
//        输出adjList
        System.out.println("adjList: " + adjList.toString());
//        2.创建入度数组-记录每门课程还需要多少先修课程
        int[] inDegree = new int[numCourses];
        System.out.println("入度数组: ");
        System.out.println(Arrays.toString(inDegree) );
//        3.填充邻接表和入度数组
        for(int[] prereq: prerequisites) {
            int course = prereq[0];
            int pre = prereq[1];
//            添加每个课程依赖的课程(邻接关系)
            adjList.get(pre).add(course);
            //每个课程顶点的入度
            inDegree[course]++;
        }
        //
        System.out.println("adjList: " + adjList.toString());
        System.out.println("入度数组: ");
        System.out.println(Arrays.toString(inDegree) );
        // 4. 初始化队列(添加所有入度为0的节点)-存储当前可以学习的课程
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
// 向队列中添加元素
                queue.offer(i);
            }
        }
        System.out.println("可以学习的课程: " + queue.toString());
        // 5. 拓扑排序
        int count = 0;
        while (!queue.isEmpty()) {
//            从队列头部移除并返回元素--从队列中取出课程学习
            int current = queue.poll();
            count++;

            // 遍历当前节点的所有邻居---将入度变为0的课程加入队列
            for (int neighbor : adjList.get(current)) {
//                更新该课程所有后继课程的入度
                inDegree[neighbor]--;
                System.out.println("更新后的入度:"+Arrays.toString(inDegree) );
                if (inDegree[neighbor] == 0) {
                    queue.offer(neighbor);
                    System.out.println("可以学习的课程: " + queue.toString());
                }
            }
        }

        // 6. 检查是否所有课程都能完成
        return count == numCourses;
    }
    public static void main(String[] args) {
        Course solution = new Course();

        // 创建依赖图
        int numCourses = 8;
        int[][] prerequisites = {
                {1, 0}, {2, 0}, {3, 1}, {3, 2},
                {4, 3}, {5, 3}, {6, 4}, {6, 5},
                {7, 6}
        };
        System.out.println(
               "是否可以完成:"+ solution.isFinish(numCourses, prerequisites)
        );
    }
}

8.给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
思路:因为链表是单方向的,所以使用双指针,第一个指针先从头节点走n步,然后第二个指针开始从头部向后走,同时,第一个指针继续向后直到结尾。第二个指针再进行删除操作即可(slow.next = slow.next.next)。

// 链表节点定义
class ListNode {
    int val;
    ListNode next;

    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }

    // 辅助方法:打印链表
    public static void printList(ListNode head) {
        ListNode curr = head;
        while (curr != null) {
            System.out.print(curr.val + " -> ");
            curr = curr.next;
        }
        System.out.println("null");
    }

    // 辅助方法:通过数组创建链表
    public static ListNode createList(int[] arr) {
        if (arr == null || arr.length == 0) return null;
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        for (int num : arr) {
            curr.next = new ListNode(num);
            curr = curr.next;
        }
        return dummy.next;
    }
}


class LinkedListDemo {
    // 方法一:双指针法(最优)
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        ListNode fast = dummy;
        ListNode slow = dummy;

        // 快指针先走n+1步
        for (int i = 0; i <= n; i++) {
            fast = fast.next;
        }

        // 快慢指针同时前进
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }

        // 删除节点
        slow.next = slow.next.next;
        return dummy.next;
    }


    public static void main(String[] args) {
        LinkedListDemo solution = new LinkedListDemo();
        // 测试用例1
        System.out.println("测试用例1:");
        int[] arr1 = {1, 2, 3, 4, 5};
        ListNode head1 = ListNode.createList(arr1);
        System.out.print("原链表: ");
        ListNode.printList(head1);
    }
}

数组快速排序
9.给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
思路:第k大等价于第n-k+1小,通过快速排序法解决问题。
快速排序法过程:
1.随机取数组中一个元素作为基准元素,比它小的放左边,比它大的放右边
2.判断基准元素的下标,相等则结束,若小于n-k,则在其右边部分继续执行步骤1,否则在左边
3.直到找到第n-k+1小的元素的下标

import java.util.Random;
class MaxEle{
	private Random random = new Random;
	 /**
     * 快速选择算法:在数组nums[left...right]中寻找第k小的元素
     * @param nums 数组
     * @param left 当前搜索范围的左边界
     * @param right 当前搜索范围的右边界
     * @param kSmallest 要寻找的第k小的索引位置(0-based)
     * @return nums中第k小的元素值
     */
     private int quickSelect(int[] nums,int left,int right,int k){
			if(left == right){
				return nums[left];
			}
			// 随机选择基准元素索引
			int pivotIndex = left + random.nextInt(right-left+1);
			// 返回基准元素的位置
			pivotIndex = partition(nums,left,right,pivotIndex);
				// 3. 根据基准元素位置决定下一步搜索方向
	       if (kSmallest == pivotIndex) {
	            // 基准元素正好是第k小的元素
	            return nums[kSmallest];
	        } else if (kSmallest < pivotIndex) {
	            // 第k小的元素在基准元素的左侧,继续在左半部分搜索
	            return quickSelect(nums, left, pivotIndex - 1, kSmallest);
	        } else {
	            // 第k小的元素在基准元素的右侧,继续在右半部分搜索
	            return quickSelect(nums, pivotIndex + 1, right, kSmallest);
	        }
		}

		 /**
     * 分区操作:将数组重新排列,使得所有小于基准的元素在基准左侧,
     * 所有大于等于基准的元素在基准右侧
     *
     * @param nums 数组
     * @param left 当前分区范围的左边界
     * @param right 当前分区范围的右边界
     * @param pivotIndex 选择的基准元素的初始索引
     * @return 基准元素在分区后的最终索引位置
     */
    private int partition(int[] nums, int left, int right, int pivotIndex) {
        // 1. 保存基准元素的值
        int pivotValue = nums[pivotIndex];

        // 2. 将基准元素交换到当前范围的最后位置(right索引处)
        // 这样我们可以从左到右扫描,将小于基准的元素移到左侧
        swap(nums, pivotIndex, right);

        // 3. storeIndex指针:指向下一个小于基准的元素应该放置的位置
        int storeIndex = left;

        // 4. 扫描整个范围(除了最后的基准元素)
        for (int i = left; i < right; i++) {
            if (nums[i] < pivotValue) {
                // 当前元素小于基准,将其交换到storeIndex位置
                swap(nums, storeIndex, i);
                storeIndex++; // 移动指针,指向下一个位置
            }
        }
        // 5. 将基准元素从最后位置交换到它正确的位置(storeIndex)
        // 此时,storeIndex左侧的所有元素都小于基准值
        swap(nums, storeIndex, right);
        // 6. 返回基准元素的最终位置
        return storeIndex;
    }
	 /**
     * 交换数组中两个位置的元素
     * @param nums 数组
     * @param i 第一个索引
     * @param j 第二个索引
     */
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
	  public int findKthLargest(int[] nums, int k) {
	        // 将问题转化为寻找第(n-k+1)小的元素(从小到大排序)
	        // 因为第k大 = 第(n-k+1)小(数组长度n)
	        // 这里使用0-based索引,所以是nums.length - k
	        return quickSelect(nums, 0, nums.length - 1, nums.length - k);
	    }
	    // 测试
	    public static void main(String[] args) {
	        MaxEle maxEle = new MaxEle();
	        int[] nums = new int[10];
	        for (int i = 0; i < 10; i++) {
	            nums[i] = i;
	        }
	        System.out.println(maxEle.findKthLargest(nums, 6));
    }
}

链表归并排序
10.给你链表的头结点 head ,将其按升序排列并返 排序后的链表 。
对于链表的归并排序,思路如下:

  • 找到链表的中点,将链表拆分成两个子链表。
  • 对两个子链表分别进行排序。
  • 将两个排序后的子链表合并。
    时间复杂度O(nlogn):
    分割:对于一个长度为 n 的序列,分割的次数为 log₂ n(以2为底的对数)
    合并:合并时每一层遍历的次数为n
class Soluton{
	public:
		ListNode* sortList(ListNode* head){
			// 节点为空或单个节点则终止
			if(!head || !head->next) return head;
			ListNode *slow = head,*fast = head->next;
			// 找到中点
			while(fast&&fast->next){
				slow = slow->next;
				fast = fast->next->next;
			}
			// 分割链表为两部分
			ListNode *mid = slow->next;
			slow->next = nullptr;
			// 递归排序左右两部分
			ListNode *left = sortList(head);
			ListNode *right = sortList(head);
	
			return merge(left,right);
		}
	private:
		// 合并有序链表
		ListNode* merge(ListNode* l1,listNode *L2){
			List dummy(0);
			listNode *tail = &dummy;
			while(l1&&l2){
				if(l1->val <= l2->val){
					tail->next = l1;
					l1 = l1->next;
				}else{
					tail->next = l2;
					l2 = l2->next;
				}
				tail = tail->next;
			}
			//连接剩余部分
			tail ->next = l1?l1:l2;
			return dummy.next;
		}			
}
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, com.demos.ListNode next) { this.val = val; this.next = next; }
}
class LinkSort {
    public ListNode sortList(ListNode head) {
        // 递归终止条件
        if (head == null || head.next == null) {
            return head;
        }

        // 使用快慢指针找到链表中点
        ListNode slow = head;
        ListNode fast = head.next;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        // 分割链表
        ListNode mid = slow.next;
        slow.next = null;

        // 递归排序左右两部分
        ListNode left = sortList(head);
        ListNode right = sortList(mid);

        // 合并两个有序链表
        System.out.println(left.val + " ," + right.val);
        return merge(left, right);
    }

    // 合并两个有序链表
    private ListNode merge(ListNode l1, ListNode l2) {

        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;

        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                l1 = l1.next;
            } else {
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }

        // 处理剩余节点
        if (l1 != null) {
            cur.next = l1;
        } else {
            cur.next = l2;
        }
        return dummy.next;
        
    }
    public static void main(String[] args) {
        // 创建链表:4 -> 2 -> 1 -> 3
        ListNode head = new ListNode(4);
        head.next = new ListNode(2);
        head.next.next = new ListNode(1);
        head.next.next.next = new ListNode(3);

        LinkSort solution = new LinkSort();
        ListNode sorted = solution.sortList(head);

        // 打印排序后的链表:1 -> 2 -> 3 -> 4
        while (sorted != null) {
            System.out.print(sorted.val + " ");
            sorted = sorted.next;
        }
    }
}

根据遍历顺序递归构建树
11.根据先序排序和中序排序生成树并确定根元素
思路:
1.根据先序遍历顺序确定根元素
2.根据这个根元素在中序遍历中的位置分为左子树和右子树
3.分别遍历左子树和右子树,递归执行以上两步

import java.util.HashMap;
import java.util.Map;
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;
    }
}
// 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
public class TreeGenerate {

    private Map<Integer, Integer> inorderIndexMap;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder == null || inorder == null || preorder.length != inorder.length) {
            return null;
        }

        // 构建中序遍历值到索引的映射,便于快速查找根节点位置
        Map<Integer, Integer> inorderMap = new HashMap<>();
        for (int i = 0; i < inorder.length; i++) {
            inorderMap.put(inorder[i], i);
        }

        return buildTreeHelper(preorder, 0, preorder.length - 1,
                inorder, 0, inorder.length - 1, inorderMap);
    }

    // 递归构建二叉树的辅助函数
    private TreeNode buildTreeHelper(int[] preorder, int preStart, int preEnd,
                                     int[] inorder, int inStart, int inEnd,
                                     Map<Integer, Integer> inorderMap) {
        // 递归终止条件
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }

        // 前序遍历的第一个元素是根节点
        TreeNode root = new TreeNode(preorder[preStart]);

        // 在中序遍历中找到根节点的位置
        int rootIndex = inorderMap.get(root.val);

        // 计算左子树的节点个数
        int leftTreeSize = rootIndex - inStart;

        // 递归构建左子树
        // 左子树在前序遍历中的范围: [preStart + 1, preStart + leftTreeSize]
        // 左子树在中序遍历中的范围: [inStart, rootIndex - 1]
        root.left = buildTreeHelper(preorder, preStart + 1, preStart + leftTreeSize,
                inorder, inStart, rootIndex - 1, inorderMap);
        // 递归构建右子树
        // 右子树在前序遍历中的范围: [preStart + leftTreeSize + 1, preEnd]
        // 右子树在中序遍历中的范围: [rootIndex + 1, inEnd]
        root.right = buildTreeHelper(preorder, preStart + leftTreeSize + 1, preEnd,
                inorder, rootIndex + 1, inEnd, inorderMap);
        System.out.println(root.val);

        //    递归最深的地方(叶子节点)会最先执行 return root
//    然后逐层向上返回
        return root;
    }

    public static void main(String[] args) {
        TreeGenerate treeGenerate = new TreeGenerate();
        // 测试用例1: 标准二叉树
        System.out.println("=== 测试用例1: 标准二叉树 ===");
        int[] preorder1 = {3, 9, 20, 15, 7};
        int[] inorder1 = {9, 3, 15, 20, 7};
        TreeNode root1 = treeGenerate.buildTree(preorder1, inorder1);
        System.out.println(root1.val);
    }
}

求二叉搜索树中与给定数值差值最小的节点。
12. 计一个高效的算法来找到二叉搜索树中与给定值 k 差值绝对值最小的节点。
思路:
计算当前节点与k差值的绝对值
1.从根节点开始遍历并更新最小差值
2.若K<根元素,则遍历其左子树
3.若K>根元素,则遍历其右子树
4.若K = 根元素,则直接结束

typedef struct BinarySearchNode
{
    int data;
    struct BinarySearchNode* left;// 注意:这里不能写 BiTreeNode* left (在C语言中)
    struct BinarySearchNode* right;
}BinarySearchNode;

typedef BinarySearchNode* BiTree; 
void searchX(BiTree root, int k){
	if(!root){
		cout << "树为空!" << end1;
		return;
	}

  BiTree current = root;//当前节点
  BiTree bestNode = root;//最佳节点
  int minDiff = abs(root->data - k);

	while(current){
		int currentDiff =  abs(current->data -k);
		// 更新最优解
		if(currentDiff < minDiff){
			minDiff = currentDiff;
			bestNode = current;
		}
		// 如果找到完全相等的值,提前结束
        if (currentDiff == 0) {
            break;
        }
        // 根据BST特性决定搜索方向
        if (k < current->data) {
            current = current->left;  // 只需要搜索左子树
        } else {
            current = current->right; // 只需要搜索右子树
        }
	}
	 cout << "与 k 差值绝对值最小的节点值: " << bestNode->data << endl;
    cout << "最小绝对差值: " << minDiff << endl;
}

二叉树遍历
13.给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
思路:深度遍历或广度遍历(每层1个节点)
深度遍历:
1.创建列表容器,压入根节点
2.先遍历当前节点的右子树,若树的深度 等于 列表长度,则压入容器(深度优先)
3.再遍历当前节点的左子树,若树的深度 等于 列表长度,则压入容器

public class RightValues {
    // 二叉树节点定义
    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    //   DFS递归
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        dfs(root, 0, res);
        return res;
    }

    private void dfs(TreeNode node, int depth, List<Integer> res) {
        if (node == null) return;
//        第一个访问的节点,depth=0
        if (depth == res.size()) res.add(node.val);
//        先递归右子树,再递归左子树
        dfs(node.right, depth + 1, res);
        dfs(node.left, depth + 1, res);
    }

    public static void main(String[] args) {
        // 构建测试二叉树
        //       1
        //      / \
        //     2   3
        //      \   \
        //       5   4
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.right = new TreeNode(5);
        root.right.right = new TreeNode(4);
        RightValues solution = new RightValues();
        // 测试BFS方法
        List<Integer> result1 = solution.rightSideView(root);
        System.out.println("BFS结果: " + result1);
    }
}
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐