算法数据结构入门篇章

1. (入门)语言选择

package class001;

// 测试链接 : https://leetcode.cn/problems/sort-an-array/
// 此时不要求掌握,因为这些排序后续的课都会讲到的
// 这里只是想说明代码语言的转换并不困难
// 整个系列虽然都是java讲的,但使用不同语言的同学听懂思路之后,想理解代码真的不是问题
// 语言问题并不是学习算法的障碍,有了人工智能工具之后,就更不是障碍了
public class LanguageConversion {

	class Solution {

		public static int[] sortArray(int[] nums) {
			if (nums.length > 1) {
				mergeSort(nums);
			}
			return nums;
		}

		public static int MAXN = 50001;

		// 以下是归并排序
		public static int[] help = new int[MAXN];

		public static void mergeSort(int[] arr) {
			int n = arr.length;
			for (int l, m, r, step = 1; step < n; step <<= 1) {
				l = 0;
				while (l < n) {
					m = l + step - 1;
					if (m + 1 >= n) {
						break;
					}
					r = Math.min(l + (step << 1) - 1, n - 1);
					merge(arr, l, m, r);
					l = r + 1;
				}
			}
		}

		public static void merge(int[] nums, int l, int m, int r) {
			int p1 = l;
			int p2 = m + 1;
			int i = l;
			while (p1 <= m && p2 <= r) {
				help[i++] = nums[p1] <= nums[p2] ? nums[p1++] : nums[p2++];
			}
			while (p1 <= m) {
				help[i++] = nums[p1++];
			}
			while (p2 <= r) {
				help[i++] = nums[p2++];
			}
			for (i = l; i <= r; i++) {
				nums[i] = help[i];
			}
		}

		// 以下是随机快速排序
		public static void quickSort(int[] arr) {
			sort(arr, 0, arr.length - 1);
		}

		public static void sort(int[] arr, int l, int r) {
			if (l >= r) {
				return;
			}
			// 随机这一下,常数时间比较大
			// 但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O(n * logn)
			int x = arr[l + (int) (Math.random() * (r - l + 1))];
			partition(arr, l, r, x);
			int left = first;
			int right = last;
			sort(arr, l, left - 1);
			sort(arr, right + 1, r);
		}

		public static int first, last;

		public static void partition(int[] nums, int l, int r, int x) {
			first = l;
			last = r;
			int i = l;
			while (i <= last) {
				if (nums[i] == x) {
					i++;
				} else if (nums[i] < x) {
					swap(nums, first++, i++);
				} else {
					swap(nums, i, last--);
				}
			}
		}

		public static void swap(int[] arr, int i, int j) {
			int tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}

		// 以下是堆排序
		public static void heapSort(int[] nums) {
			int n = nums.length;
			for (int i = n - 1; i >= 0; i--) {
				heapify(nums, i, n);
			}
			while (n > 1) {
				swap(nums, 0, --n);
				heapify(nums, 0, n);
			}
		}

		// 这个方法虽然堆排序用不上,但是堆结构里是重要方法,所以这里保留
		// 后面的课会讲
		public static void heapInsert(int[] nums, int i) {
			while (nums[i] > nums[(i - 1) / 2]) {
				swap(nums, i, (i - 1) / 2);
				i = (i - 1) / 2;
			}
		}

		public static void heapify(int[] nums, int i, int s) {
			int l = i * 2 + 1;
			while (l < s) {
				int best = l + 1 < s && nums[l + 1] > nums[l] ? l + 1 : l;
				best = nums[best] > nums[i] ? best : i;
				if (best == i) {
					break;
				}
				swap(nums, best, i);
				i = best;
				l = i * 2 + 1;
			}
		}

	}

}

2. (入门)从社会实验到入门提醒

package class002;

import java.util.Arrays;

// 一开始有100个人,每个人都有100元
// 在每一轮都做如下的事情 : 
// 每个人都必须拿出1元钱给除自己以外的其他人,给谁完全随机
// 如果某个人在这一轮的钱数为0,那么他可以不给,但是可以接收
// 发生很多很多轮之后,这100人的社会财富分布很均匀吗?
public class Experiment {

	public static void main(String[] args) {
		System.out.println("一个社会的基尼系数是一个在0~1之间的小数");
		System.out.println("基尼系数为0代表所有人的财富完全一样");
		System.out.println("基尼系数为1代表有1个人掌握了全社会的财富");
		System.out.println("基尼系数越小,代表社会财富分布越均衡;越大则代表财富分布越不均衡");
		System.out.println("在2022年,世界各国的平均基尼系数为0.44");
		System.out.println("目前普遍认为,当基尼系数到达 0.5 时");
		System.out.println("就意味着社会贫富差距非常大,分布非常不均匀");
		System.out.println("社会可能会因此陷入危机,比如大量的犯罪或者经历社会动荡");
		System.out.println("测试开始");
		int n = 100;
		int t = 1000000;
		System.out.println("人数 : " + n);
		System.out.println("轮数 : " + t);
		experiment(n, t);
		System.out.println("测试结束");
	}

	// 完全按照说的来实验
	public static void experiment(int n, int t) {
		double[] wealth = new double[n];
		Arrays.fill(wealth, 100);
		boolean[] hasMoney = new boolean[n];
		for (int i = 0; i < t; i++) {
			Arrays.fill(hasMoney, false);
			for (int j = 0; j < n; j++) {
				if (wealth[j] > 0) {
					hasMoney[j] = true;
				}
			}
			for (int j = 0; j < n; j++) {
				if (hasMoney[j]) {
					int other = j;
					do {
						// (int) (Math.random() * n);
						// int : 0 ~ n-1,等概率随机
						other = (int) (Math.random() * n);
					} while (other == j);
					wealth[j]--;
					wealth[other]++;
				}
			}
		}
		Arrays.sort(wealth);
		System.out.println("列出每个人的财富(贫穷到富有) : ");
		for (int i = 0; i < n; i++) {
			System.out.print((int) wealth[i] + " ");
			if (i % 10 == 9) {
				System.out.println();
			}
		}
		System.out.println();
		System.out.println("这个社会的基尼系数为 : " + calculateGini(wealth));
	}

	// 计算基尼系数
	// 看代码就可以轻易知道怎么算的
	public static double calculateGini(double[] wealth) {
		double sumOfAbsoluteDifferences = 0;
		double sumOfWealth = 0;
		int n = wealth.length;
		for (int i = 0; i < n; i++) {
			sumOfWealth += wealth[i];
			for (int j = 0; j < n; j++) {
				sumOfAbsoluteDifferences += Math.abs(wealth[i] - wealth[j]);
			}
		}
		return sumOfAbsoluteDifferences / (2 * n * sumOfWealth);
	}

}

3. (入门)二进制与位运算

package class003;

// 本文件的实现是用int来举例的
// 对于long类型完全同理
// 不过要注意,如果是long类型的数字num,有64位
// num & (1 << 48),这种写法不对
// 因为1是一个int类型,只有32位,所以(1 << 48)早就溢出了,所以无意义
// 应该写成 : num & (1L << 48)
public class BinarySystem {

	// 打印一个int类型的数字,32位进制的状态
	// 左侧是高位,右侧是低位
	public static void printBinary(int num) {
		for (int i = 31; i >= 0; i--) {
			// 下面这句写法,可以改成 :
			// System.out.print((a & (1 << i)) != 0 ? "1" : "0");
			// 但不可以改成 :
			// System.out.print((a & (1 << i)) == 1 ? "1" : "0");
			// 因为a如果第i位有1,那么(a & (1 << i))是2的i次方,而不一定是1
			// 比如,a = 0010011
			// a的第0位是1,第1位是1,第4位是1
			// (a & (1<<4)) == 16(不是1),说明a的第4位是1状态
			System.out.print((num & (1 << i)) == 0 ? "0" : "1");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		// 非负数
		int a = 78;
		System.out.println(a);
		printBinary(a);
		System.out.println("===a===");
		// 负数
		int b = -6;
		System.out.println(b);
		printBinary(b);
		System.out.println("===b===");
		// 直接写二进制的形式定义变量
		int c = 0b1001110;
		System.out.println(c);
		printBinary(c);
		System.out.println("===c===");
		// 直接写十六进制的形式定义变量
		// 0100 -> 4
		// 1110 -> e
		// 0x4e -> 01001110
		int d = 0x4e;
		System.out.println(d);
		printBinary(d);
		System.out.println("===d===");
		// ~、相反数
		System.out.println(a);
		printBinary(a);
		printBinary(~a);
		int e = ~a + 1;
		System.out.println(e);
		printBinary(e);
		System.out.println("===e===");
		// int、long的最小值,取相反数、绝对值,都是自己
		int f = Integer.MIN_VALUE;
		System.out.println(f);
		printBinary(f);
		System.out.println(-f);
		printBinary(-f);
		System.out.println(~f + 1);
		printBinary(~f + 1);
		System.out.println("===f===");
		// | & ^
		int g = 0b0001010;
		int h = 0b0001100;
		printBinary(g | h);
		printBinary(g & h);
		printBinary(g ^ h);
		System.out.println("===g、h===");
		// 可以这么写 : int num = 3231 | 6434;
		// 可以这么写 : int num = 3231 & 6434;
		// 不能这么写 : int num = 3231 || 6434;
		// 不能这么写 : int num = 3231 && 6434;
		// 因为 ||、&& 是 逻辑或、逻辑与,只能连接boolean类型
		// 不仅如此,|、& 连接的两侧一定都会计算
		// 而 ||、&& 有穿透性的特点
		System.out.println("test1测试开始");
		boolean test1 = returnTrue() | returnFalse();
		System.out.println("test1结果," + test1);
		System.out.println("test2测试开始");
		boolean test2 = returnTrue() || returnFalse();
		System.out.println("test2结果," + test2);
		System.out.println("test3测试开始");
		boolean test3 = returnFalse() & returnTrue();
		System.out.println("test3结果," + test3);
		System.out.println("test4测试开始");
		boolean test4 = returnFalse() && returnTrue();
		System.out.println("test4结果," + test4);
		System.out.println("===|、&、||、&&===");
		// <<
		int i = 0b0011010;
		printBinary(i);
		printBinary(i << 1);
		printBinary(i << 2);
		printBinary(i << 3);
		System.out.println("===i << ===");
		// 非负数 >> >>>,效果一样
		printBinary(i);
		printBinary(i >> 2);
		printBinary(i >>> 2);
		System.out.println("===i >> >>>===");
		// 负数 >> >>>,效果不一样
		int j = 0b11110000000000000000000000000000;
		printBinary(j);
		printBinary(j >> 2);
		printBinary(j >>> 2);
		System.out.println("===j >> >>>===");
		// 非负数 << 1,等同于乘以2
		// 非负数 << 2,等同于乘以4
		// 非负数 << 3,等同于乘以8
		// 非负数 << i,等同于乘以2的i次方
		// ...
		// 非负数 >> 1,等同于除以2
		// 非负数 >> 2,等同于除以4
		// 非负数 >> 3,等同于除以8
		// 非负数 >> i,等同于除以2的i次方
		// 只有非负数符合这个特征,负数不要用
		int k = 10;
		System.out.println(k);
		System.out.println(k << 1);
		System.out.println(k << 2);
		System.out.println(k << 3);
		System.out.println(k >> 1);
		System.out.println(k >> 2);
		System.out.println(k >> 3);
		System.out.println("===k===");
	}

	public static boolean returnTrue() {
		System.out.println("进入了returnTrue函数");
		return true;
	}

	public static boolean returnFalse() {
		System.out.println("进入了returnFalse函数");
		return false;
	}

}

4. (入门)选择冒泡插入排序

package class004;

public class SelectBubbleInsert {

	// 数组中交换i和j位置的数
	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

	// 选择排序
	public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int minIndex, i = 0; i < arr.length - 1; i++) {
			minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {
				if (arr[j] < arr[minIndex]) {
					minIndex = j;
				}
			}
			swap(arr, i, minIndex);
		}
	}

	// 冒泡排序
	public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int end = arr.length - 1; end > 0; end--) {
			for (int i = 0; i < end; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}

	// 插入排序
	public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 1; i < arr.length; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

}

5. (入门)对数器

package class005;

public class Validator {

	// 为了验证
	public static void main(String[] args) {
		// 随机数组最大长度
		int N = 200;
		// 随机数组每个值,在1~V之间等概率随机
		int V = 1000;
		// testTimes : 测试次数
		int testTimes = 50000;
		System.out.println("测试开始");
		for (int i = 0; i < testTimes; i++) {
			// 随机得到一个长度,长度在[0~N-1]
			int n = (int) (Math.random() * N);
			// 得到随机数组
			int[] arr = randomArray(n, V);
			int[] arr1 = copyArray(arr);
			int[] arr2 = copyArray(arr);
			int[] arr3 = copyArray(arr);
			selectionSort(arr1);
			bubbleSort(arr2);
			insertionSort(arr3);
			if (!sameArray(arr1, arr2) || !sameArray(arr1, arr3)) {
				System.out.println("出错了!");
				// 当有错了
				// 打印是什么例子,出错的
				// 打印三个功能,各自排序成了什么样
				// 可能要把例子带入,每个方法,去debug!
			}
		}
		System.out.println("测试结束");
	}

	// 为了验证
	// 得到一个随机数组,长度是n
	// 数组中每个数,都在1~v之间,随机得到
	public static int[] randomArray(int n, int v) {
		int[] arr = new int[n];
		for (int i = 0; i < n; i++) {
			// Math.random() -> double -> [0,1)范围山的一个小数,0.37463473126、0.001231231,等概率!
			// Math.random() * v -> double -> [0,v)一个小数,依然等概率
			// (int)(Math.random() * v) -> int -> 0 1 2 3 ... v-1,等概率的!
			// (int) (Math.random() * v) + 1 -> int -> 1 2 3 .... v,等概率的!
			arr[i] = (int) (Math.random() * v) + 1;
		}
		return arr;
	}

	// 为了验证
	public static int[] copyArray(int[] arr) {
		int n = arr.length;
		int[] ans = new int[n];
		for (int i = 0; i < n; i++) {
			ans[i] = arr[i];
		}
		return ans;
	}

	// 为了验证
	public static boolean sameArray(int[] arr1, int[] arr2) {
		int n = arr1.length;
		for (int i = 0; i < n; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// 数组中交换i和j位置的数
	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

	// 选择排序
	public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int minIndex, i = 0; i < arr.length - 1; i++) {
			minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {
				if (arr[j] < arr[minIndex]) {
					minIndex = j;
				}
			}
			swap(arr, i, minIndex);
		}
	}

	// 冒泡排序
	public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int end = arr.length - 1; end > 0; end--) {
			for (int i = 0; i < end; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}

	// 插入排序
	public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 1; i < arr.length; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

}

6. (入门)二分搜索

package class006;

import java.util.Arrays;

// 有序数组中是否存在一个数字
public class Code01_FindNumber {

	// 为了验证
	public static void main(String[] args) {
		int N = 100;
		int V = 1000;
		int testTime = 500000;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int n = (int) (Math.random() * N);
			int[] arr = randomArray(n, V);
			Arrays.sort(arr);
			int num = (int) (Math.random() * V);
			if (right(arr, num) != exist(arr, num)) {
				System.out.println("出错了!");
			}
		}
		System.out.println("测试结束");
	}

	// 为了验证
	public static int[] randomArray(int n, int v) {
		int[] arr = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = (int) (Math.random() * v) + 1;
		}
		return arr;
	}

	// 为了验证
	// 保证arr有序,才能用这个方法
	public static boolean right(int[] sortedArr, int num) {
		for (int cur : sortedArr) {
			if (cur == num) {
				return true;
			}
		}
		return false;
	}

	// 保证arr有序,才能用这个方法
	public static boolean exist(int[] arr, int num) {
		if (arr == null || arr.length == 0) {
			return false;
		}
		int l = 0, r = arr.length - 1, m = 0;
		while (l <= r) {
			m = (l + r) / 2;
			if (arr[m] == num) {
				return true;
			} else if (arr[m] > num) {
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return false;
	}

}
package class006;

import java.util.Arrays;

// 有序数组中找>=num的最左位置
public class Code02_FindLeft {

	// 为了验证
	public static void main(String[] args) {
		int N = 100;
		int V = 1000;
		int testTime = 500000;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int n = (int) (Math.random() * N);
			int[] arr = randomArray(n, V);
			Arrays.sort(arr);
			int num = (int) (Math.random() * N);
			if (right(arr, num) != findLeft(arr, num)) {
				System.out.println("出错了!");
			}
		}
		System.out.println("测试结束");
	}

	// 为了验证
	public static int[] randomArray(int n, int v) {
		int[] arr = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = (int) (Math.random() * v) + 1;
		}
		return arr;
	}

	// 为了验证
	// 保证arr有序,才能用这个方法
	public static int right(int[] arr, int num) {
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] >= num) {
				return i;
			}
		}
		return -1;
	}

	// 保证arr有序,才能用这个方法
	// 有序数组中找>=num的最左位置
	public static int findLeft(int[] arr, int num) {
		int l = 0, r = arr.length - 1, m = 0;
		int ans = -1;
		while (l <= r) {
			// m = (l + r) / 2;
			// m = l + (r - l) / 2;
			m = l + ((r - l) >> 1);
			if (arr[m] >= num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}
package class006;

import java.util.Arrays;

// 有序数组中找<=num的最右位置
public class Code03_FindRight {

	// 为了验证
	public static void main(String[] args) {
		int N = 100;
		int V = 1000;
		int testTime = 500000;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int n = (int) (Math.random() * N);
			int[] arr = randomArray(n, V);
			Arrays.sort(arr);
			int num = (int) (Math.random() * N);
			if (right(arr, num) != findRight(arr, num)) {
				System.out.println("出错了!");
			}
		}
		System.out.println("测试结束");
	}

	// 为了验证
	public static int[] randomArray(int n, int v) {
		int[] arr = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = (int) (Math.random() * v) + 1;
		}
		return arr;
	}

	// 为了验证
	// 保证arr有序,才能用这个方法
	public static int right(int[] arr, int num) {
		for (int i = arr.length - 1; i >= 0; i--) {
			if (arr[i] <= num) {
				return i;
			}
		}
		return -1;
	}

	// 保证arr有序,才能用这个方法
	// 有序数组中找<=num的最右位置
	public static int findRight(int[] arr, int num) {
		int l = 0, r = arr.length - 1, m = 0;
		int ans = -1;
		while (l <= r) {
			m = l + ((r - l) >> 1);
			if (arr[m] <= num) {
				ans = m;
				l = m + 1;
			} else {
				r = m - 1;
			}
		}
		return ans;
	}

}
package class006;

// 峰值元素是指其值严格大于左右相邻值的元素
// 给你一个整数数组 nums,已知任何两个相邻的值都不相等
// 找到峰值元素并返回其索引
// 数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
// 你可以假设 nums[-1] = nums[n] = 无穷小
// 你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
public class Code04_FindPeakElement {

	// 测试链接 : https://leetcode.cn/problems/find-peak-element/
	class Solution {

		public static int findPeakElement(int[] arr) {
			int n = arr.length;
			if (arr.length == 1) {
				return 0;
			}
			if (arr[0] > arr[1]) {
				return 0;
			}
			if (arr[n - 1] > arr[n - 2]) {
				return n - 1;
			}
			int l = 1, r = n - 2, m = 0, ans = -1;
			while (l <= r) {
				m = (l + r) / 2;
				if (arr[m - 1] > arr[m]) {
					r = m - 1;
				} else if (arr[m] < arr[m + 1]) {
					l = m + 1;
				} else {
					ans = m;
					break;
				}
			}
			return ans;
		}

	}

}

7. (入门)时间复杂度与空间复杂度

package class007;

import java.util.ArrayList;

public class Complexity {

	// 只用一个循环完成冒泡排序
	// 但这是时间复杂度O(N^2)的!
	public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		int n = arr.length;
		int end = n - 1, i = 0;
		while (end > 0) {
			if (arr[i] > arr[i + 1]) {
				swap(arr, i, i + 1);
			}
			if (i < end - 1) {
				i++;
			} else {
				end--;
				i = 0;
			}
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

	public static void main(String[] args) {
		// 随机生成长度为n
		// 值在0~v-1之间
		// 且任意相邻两数不相等的数组
		int n = 10;
		int v = 4;
		int[] arr1 = new int[n];
		arr1[0] = (int) (Math.random() * v);
		for (int i = 1; i < n; i++) {
			do {
				arr1[i] = (int) (Math.random() * v);
			} while (arr1[i] == arr1[i - 1]);
		}
		for (int num : arr1) {
			System.out.print(num + " ");
		}
		System.out.println();
		System.out.println("=========");

		// java中的动态数组是ArrayList
		// 各个语言中的动态数组的初始大小和实际扩容因子可能会变化,但是均摊都是O(1)
		// 课上用2作为扩容因子只是举例而已
		ArrayList<Integer> arr2 = new ArrayList<>();
		arr2.add(5); // 0
		arr2.add(4); // 1
		arr2.add(9); // 2
		arr2.set(1, 6); // arr[1]由4改成了6
		System.out.println(arr2.get(1));
		System.out.println("=========");

		int[] arr = { 64, 31, 78, 0, 5, 7, 103 };
		bubbleSort(arr);
		for (int num : arr) {
			System.out.print(num + " ");
		}
		System.out.println();
		System.out.println("=========");

		int N = 200000;
		long start;
		long end;
		System.out.println("测试开始");
		start = System.currentTimeMillis();
		for (int i = 1; i <= N; i++) {
			for (int j = i; j <= N; j += i) {
				// 这两个嵌套for循环的流程,时间复杂度为O(N * logN)
				// 1/1 + 1/2 + 1/3 + 1/4 + 1/5 + ... + 1/n,也叫"调和级数",收敛于O(logN)
				// 所以如果一个流程的表达式 : n/1 + n/2 + n/3 + ... + n/n
				// 那么这个流程时间复杂度O(N * logN)
			}
		}
		end = System.currentTimeMillis();
		System.out.println("测试结束,运行时间 : " + (end - start) + " 毫秒");

		System.out.println("测试开始");
		start = System.currentTimeMillis();
		for (int i = 1; i <= N; i++) {
			for (int j = i; j <= N; j++) {
				// 这两个嵌套for循环的流程,时间复杂度为O(N^2)
				// 很明显等差数列
			}
		}
		end = System.currentTimeMillis();
		System.out.println("测试结束,运行时间 : " + (end - start) + " 毫秒");

	}

}

8. (入门)算法与数据结构简介

9. (入门)单双链表及其反转-堆栈诠释

package class009;

// 按值传递、按引用传递
// 从堆栈角度解释链表节点
// 以堆栈视角来看链表反转
public class ListReverse {

	public static void main(String[] args) {
		// int、long、byte、short
		// char、float、double、boolean
		// 还有String
		// 都是按值传递
		int a = 10;
		f(a);
		System.out.println(a);

		// 其他类型按引用传递
		// 比如下面的Number是自定义的类
		Number b = new Number(5);
		g1(b);
		System.out.println(b.val);
		g2(b);
		System.out.println(b.val);

		// 比如下面的一维数组
		int[] c = { 1, 2, 3, 4 };
		g3(c);
		System.out.println(c[0]);
		g4(c);
		System.out.println(c[0]);
	}

	public static void f(int a) {
		a = 0;
	}

	public static class Number {
		public int val;

		public Number(int v) {
			val = v;
		}
	}

	public static void g1(Number b) {
		b = null;
	}

	public static void g2(Number b) {
		b.val = 6;
	}

	public static void g3(int[] c) {
		c = null;
	}

	public static void g4(int[] c) {
		c[0] = 100;
	}

	// 单链表节点
	public static class ListNode {
		public int val;
		public ListNode next;

		public ListNode(int val) {
			this.val = val;
		}

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

	// 反转单链表测试链接 : https://leetcode.cn/problems/reverse-linked-list/
	class Solution {

		public static ListNode reverseList(ListNode head) {
			ListNode pre = null;
			ListNode next = null;
			while (head != null) {
				next = head.next;
				head.next = pre;
				pre = head;
				head = next;
			}
			return pre;
		}

	}

	// 双链表节点
	public static class DoubleListNode {
		public int value;
		public DoubleListNode last;
		public DoubleListNode next;

		public DoubleListNode(int v) {
			value = v;
		}
	}

	// 反转双链表
	// 没有找到测试链接
	// 如下方法是对的
	public static DoubleListNode reverseDoubleList(DoubleListNode head) {
		DoubleListNode pre = null;
		DoubleListNode next = null;
		while (head != null) {
			next = head.next;
			head.next = pre;
			head.last = next;
			pre = head;
			head = next;
		}
		return pre;
	}

}

10. (入门)链表入门-合并有序链表

package class010;

// 将两个升序链表合并为一个新的 升序 链表并返回
// 新链表是通过拼接给定的两个链表的所有节点组成的
// 测试链接 : https://leetcode.cn/problems/merge-two-sorted-lists/
public class MergeTwoLists {

	// 不要提交这个类
	public static class ListNode {
		public int val;
		public ListNode next;

		public ListNode(int val) {
			this.val = val;
		}

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

	class Solution {

		public static ListNode mergeTwoLists(ListNode head1, ListNode head2) {
			if (head1 == null || head2 == null) {
				return head1 == null ? head2 : head1;
			}
			ListNode head = head1.val <= head2.val ? head1 : head2;
			ListNode cur1 = head.next;
			ListNode cur2 = head == head1 ? head2 : head1;
			ListNode pre = head;
			while (cur1 != null && cur2 != null) {
				if (cur1.val <= cur2.val) {
					pre.next = cur1;
					cur1 = cur1.next;
				} else {
					pre.next = cur2;
					cur2 = cur2.next;
				}
				pre = pre.next;
			}
			pre.next = cur1 != null ? cur1 : cur2;
			return head;
		}

	}

}

11. (入门)链表入门-链表相加

package class011;

// 给你两个 非空 的链表,表示两个非负的整数
// 它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字
// 请你将两个数相加,并以相同形式返回一个表示和的链表。
// 你可以假设除了数字 0 之外,这两个数都不会以 0 开头
// 测试链接:https://leetcode.cn/problems/add-two-numbers/
public class AddTwoNumbers {

	// 不要提交这个类
	public static class ListNode {
		public int val;
		public ListNode next;

		public ListNode(int val) {
			this.val = val;
		}

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

	class Solution {

		// 也可以复用老链表
		// 不过这个实现没有这么做,都是生成的新节点(为了教学好懂)
		public static ListNode addTwoNumbers(ListNode h1, ListNode h2) {
			ListNode ans = null, cur = null;
			int carry = 0;
			for (int sum, val; // 声明变量
					h1 != null || h2 != null; // 终止条件
					h1 = h1 == null ? null : h1.next, // 每一步h1的跳转
					h2 = h2 == null ? null : h2.next // 每一步h2的跳转
					) {

				sum = (h1 == null ? 0 : h1.val)
						+ (h2 == null ? 0 : h2.val)
						+ carry;

				val = sum % 10;
				carry = sum / 10;
				if (ans == null) {
					ans = new ListNode(val);
					cur = ans;
				} else {
					cur.next = new ListNode(val);
					cur = cur.next;
				}
			}
			if (carry == 1) {
				cur.next = new ListNode(1);
			}
			return ans;
		}

	}

}

12. (入门)链表入门-划分链表

package class012;

// 给你一个链表的头节点 head 和一个特定值 x
// 请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
// 你应当 保留 两个分区中每个节点的初始相对位置
// 测试链接 : https://leetcode.cn/problems/partition-list/
public class PartitionList {

	// 不要提交这个类
	public static class ListNode {
		public int val;
		public ListNode next;

		public ListNode(int val) {
			this.val = val;
		}

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

	class Solution {

		public static ListNode partition(ListNode head, int x) {
			ListNode leftHead = null, leftTail = null; // < x的区域
			ListNode rightHead = null, rightTail = null; // >=x的区域
			ListNode next = null;
			while (head != null) {
				next = head.next;
				head.next = null;
				if (head.val < x) {
					if (leftHead == null) {
						leftHead = head;
					} else {
						leftTail.next = head;
					}
					leftTail = head;
				} else {
					if (rightHead == null) {
						rightHead = head;
					} else {
						rightTail.next = head;
					}
					rightTail = head;
				}
				head = next;
			}
			if (leftHead == null) {
				return rightHead;
			}
			// < x的区域有内容!
			leftTail.next = rightHead;
			return leftHead;
		}

	}

}

13. (入门)队列和栈-链表数组实现

package class013;

import java.util.Queue;
import java.util.LinkedList;
import java.util.Stack;

public class QueueStackAndCircularQueue {

	// 直接用java内部的实现
	// 其实内部就是双向链表,常数操作慢
	public static class Queue1 {

		// java中的双向链表LinkedList
		// 单向链表就足够了
		public Queue<Integer> queue = new LinkedList<>();

		// 调用任何方法之前,先调用这个方法来判断队列内是否有东西
		public boolean isEmpty() {
			return queue.isEmpty();
		}

		// 向队列中加入num,加到尾巴
		public void offer(int num) {
			queue.offer(num);
		}

		// 从队列拿,从头拿
		public int poll() {
			return queue.poll();
		}

		// 返回队列头的元素但是不弹出
		public int peek() {
			return queue.peek();
		}

		// 返回目前队列里有几个数
		public int size() {
			return queue.size();
		}

	}

	// 实际刷题时更常见的写法,常数时间好
	// 如果可以确定加入操作的总次数不超过n,那么可以用
	// 一般笔试、面试都会有一个明确数据量,所以这是最常用的方式
	public static class Queue2 {

		public int[] queue;
		public int l;
		public int r;

		// 加入操作的总次数上限是多少,一定要明确
		public Queue2(int n) {
			queue = new int[n];
			l = 0;
			r = 0;
		}

		// 调用任何方法之前,先调用这个方法来判断队列内是否有东西
		public boolean isEmpty() {
			return l == r;
		}

		public void offer(int num) {
			queue[r++] = num;
		}

		public int poll() {
			return queue[l++];
		}
		// ?
		// l...r-1 r
		// [l..r)
		public int head() {
			return queue[l];
		}

		public int tail() {
			return queue[r - 1];
		}

		public int size() {
			return r - l;
		}

	}

	// 直接用java内部的实现
	// 其实就是动态数组,不过常数时间并不好
	public static class Stack1 {

		public Stack<Integer> stack = new Stack<>();

		// 调用任何方法之前,先调用这个方法来判断栈内是否有东西
		public boolean isEmpty() {
			return stack.isEmpty();
		}

		public void push(int num) {
			stack.push(num);
		}

		public int pop() {
			return stack.pop();
		}

		public int peek() {
			return stack.peek();
		}

		public int size() {
			return stack.size();
		}

	}

	// 实际刷题时更常见的写法,常数时间好
	// 如果可以保证同时在栈里的元素个数不会超过n,那么可以用
	// 也就是发生弹出操作之后,空间可以复用
	// 一般笔试、面试都会有一个明确数据量,所以这是最常用的方式
	public static class Stack2 {

		public int[] stack;
		public int size;

		// 同时在栈里的元素个数不会超过n
		public Stack2(int n) {
			stack = new int[n];
			size = 0;
		}

		// 调用任何方法之前,先调用这个方法来判断栈内是否有东西
		public boolean isEmpty() {
			return size == 0;
		}

		public void push(int num) {
			stack[size++] = num;
		}

		public int pop() {
			return stack[--size];
		}

		public int peek() {
			return stack[size - 1];
		}

		public int size() {
			return size;
		}

	}

	// 设计循环队列
	// 测试链接 : https://leetcode.cn/problems/design-circular-queue/
	class MyCircularQueue {

		public int[] queue;

		public int l, r, size, limit;

		// 同时在队列里的数字个数,不要超过k
		public MyCircularQueue(int k) {
			queue = new int[k];
			l = r = size = 0;
			limit = k;
		}

		// 如果队列满了,什么也不做,返回false
		// 如果队列没满,加入value,返回true
		public boolean enQueue(int value) {
			if (isFull()) {
				return false;
			} else {
				queue[r] = value;
				// r++, 结束了,跳回0
				r = r == limit - 1 ? 0 : (r + 1);
				size++;
				return true;
			}
		}

		// 如果队列空了,什么也不做,返回false
		// 如果队列没空,弹出头部的数字,返回true
		public boolean deQueue() {
			if (isEmpty()) {
				return false;
			} else {
				// l++, 结束了,跳回0
				l = l == limit - 1 ? 0 : (l + 1);
				size--;
				return true;
			}
		}

		// 返回队列头部的数字(不弹出),如果没有数返回-1
		public int Front() {
			if (isEmpty()) {
				return -1;
			} else {
				return queue[l];
			}
		}

		public int Rear() {
			if (isEmpty()) {
				return -1;
			} else {
				int last = r == 0 ? (limit - 1) : (r - 1);
				return queue[last];
			}
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public boolean isFull() {
			return size == limit;
		}

	}

}

14. (入门)队列和栈-队列和栈相互实现

package class014;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

// 用栈实现队列
// 用队列实现栈
public class ConvertQueueAndStack {

	// 测试链接 : https://leetcode.cn/problems/implement-queue-using-stacks/
	class MyQueue {

		public Stack<Integer> in;

		public Stack<Integer> out;

		public MyQueue() {
			in = new Stack<Integer>();
			out = new Stack<Integer>();
		}

		// 倒数据
		// 从in栈,把数据倒入out栈
		// 1) out空了,才能倒数据
		// 2) 如果倒数据,in必须倒完
		private void inToOut() {
			if (out.empty()) {
				while (!in.empty()) {
					out.push(in.pop());
				}
			}
		}

		public void push(int x) {
			in.push(x);
			inToOut();
		}

		public int pop() {
			inToOut();
			return out.pop();
		}

		public int peek() {
			inToOut();
			return out.peek();
		}

		public boolean empty() {
			return in.isEmpty() && out.isEmpty();
		}

	}

	// 测试链接 : https://leetcode.cn/problems/implement-stack-using-queues/
	class MyStack {

		Queue<Integer> queue;

		public MyStack() {
			queue = new LinkedList<Integer>();
		}

		// O(n)
		public void push(int x) {
			int n = queue.size();
			queue.offer(x);
			for (int i = 0; i < n; i++) {
				queue.offer(queue.poll());
			}
		}

		public int pop() {
			return queue.poll();
		}

		public int top() {
			return queue.peek();
		}

		public boolean empty() {
			return queue.isEmpty();
		}

	}

}

15. (入门)栈入门-最小栈

package class015;

import java.util.Stack;

// 最小栈
// 测试链接 : https://leetcode.cn/problems/min-stack/
public class GetMinStack {

	// 提交时把类名、构造方法改成MinStack
	class MinStack1 {
		public Stack<Integer> data;
		public Stack<Integer> min;

		public MinStack1() {
			data = new Stack<Integer>();
			min = new Stack<Integer>();
		}

		public void push(int val) {
			data.push(val);
			if (min.isEmpty() || val <= min.peek()) {
				min.push(val);
			} else { // !min.isEmpty() && val > min.peek()
				min.push(min.peek());
			}
		}

		public void pop() {
			data.pop();
			min.pop();
		}

		public int top() {
			return data.peek();
		}

		public int getMin() {
			return min.peek();
		}
	}

	// 提交时把类名、构造方法改成MinStack
	class MinStack2 {
		// leetcode的数据在测试时,同时在栈里的数据不超过这个值
		// 这是几次提交实验出来的,哈哈
		// 如果leetcode补测试数据了,超过这个量导致出错,就调大
		public final int MAXN = 8001;

		public int[] data;
		public int[] min;
		int size;

		public MinStack2() {
			data = new int[MAXN];
			min = new int[MAXN];
			size = 0;
		}

		public void push(int val) {
			data[size] = val;
			if (size == 0 || val <= min[size - 1]) {
				min[size] = val;
			} else {
				min[size] = min[size - 1];
			}
			size++;
		}

		public void pop() {
			size--;
		}

		public int top() {
			return data[size - 1];
		}

		public int getMin() {
			return min[size - 1];
		}
	}

}

16. (入门)双端队列-链表数组实现

package class016;

import java.util.Deque;
import java.util.LinkedList;

// 设计循环双端队列
// 测试链接 : https://leetcode.cn/problems/design-circular-deque/
public class CircularDeque {

	// 提交时把类名、构造方法改成 : MyCircularDeque
	// 其实内部就是双向链表
	// 常数操作慢,但是leetcode数据量太小了,所以看不出劣势
	class MyCircularDeque1 {

		public Deque<Integer> deque = new LinkedList<>();
		public int size;
		public int limit;

		public MyCircularDeque1(int k) {
			size = 0;
			limit = k;
		}

		public boolean insertFront(int value) {
			if (isFull()) {
				return false;
			} else {
				deque.offerFirst(value);
				size++;
				return true;
			}
		}

		public boolean insertLast(int value) {
			if (isFull()) {
				return false;
			} else {
				deque.offerLast(value);
				size++;
				return true;
			}
		}

		public boolean deleteFront() {
			if (isEmpty()) {
				return false;
			} else {
				size--;
				deque.pollFirst();
				return true;
			}
		}

		public boolean deleteLast() {
			if (isEmpty()) {
				return false;
			} else {
				size--;
				deque.pollLast();
				return true;
			}
		}

		public int getFront() {
			if (isEmpty()) {
				return -1;
			} else {
				return deque.peekFirst();
			}
		}

		public int getRear() {
			if (isEmpty()) {
				return -1;
			} else {
				return deque.peekLast();
			}
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public boolean isFull() {
			return size == limit;
		}

	}

	// 提交时把类名、构造方法改成 : MyCircularDeque
	// 自己用数组实现,常数操作快,但是leetcode数据量太小了,看不出优势
	class MyCircularDeque2 {

		public int[] deque;
		public int l, r, size, limit;

		public MyCircularDeque2(int k) {
			deque = new int[k];
			l = r = size = 0;
			limit = k;
		}

		public boolean insertFront(int value) {
			if (isFull()) {
				return false;
			} else {
				if (isEmpty()) {
					l = r = 0;
					deque[0] = value;
				} else {
					l = l == 0 ? (limit - 1) : (l - 1);
					deque[l] = value;
				}
				size++;
				return true;
			}
		}

		public boolean insertLast(int value) {
			if (isFull()) {
				return false;
			} else {
				if (isEmpty()) {
					l = r = 0;
					deque[0] = value;
				} else {
					r = r == limit - 1 ? 0 : (r + 1);
					deque[r] = value;
				}
				size++;
				return true;
			}
		}

		public boolean deleteFront() {
			if (isEmpty()) {
				return false;
			} else {
				l = (l == limit - 1) ? 0 : (l + 1);
				size--;
				return true;
			}
		}

		public boolean deleteLast() {
			if (isEmpty()) {
				return false;
			} else {
				r = r == 0 ? (limit - 1) : (r - 1);
				size--;
				return true;
			}
		}

		public int getFront() {
			if (isEmpty()) {
				return -1;
			} else {
				return deque[l];
			}
		}

		public int getRear() {
			if (isEmpty()) {
				return -1;
			} else {
				return deque[r];
			}
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public boolean isFull() {
			return size == limit;
		}

	}

}

17. (入门)二叉树及其三种序递归实现

package class017;

// 递归序的解释
// 用递归实现二叉树的三序遍历
public class BinaryTreeTraversalRecursion {

	public static class TreeNode {
		public int val;
		public TreeNode left;
		public TreeNode right;

		public TreeNode(int v) {
			val = v;
		}
	}

	// 递归基本样子,用来理解递归序
	public static void f(TreeNode head) {
		if (head == null) {
			return;
		}
		// 1
		f(head.left);
		// 2
		f(head.right);
		// 3
	}

	// 先序打印所有节点,递归版
	public static void preOrder(TreeNode head) {
		if (head == null) {
			return;
		}
		System.out.print(head.val + " ");
		preOrder(head.left);
		preOrder(head.right);
	}

	// 中序打印所有节点,递归版
	public static void inOrder(TreeNode head) {
		if (head == null) {
			return;
		}
		inOrder(head.left);
		System.out.print(head.val + " ");
		inOrder(head.right);
	}

	// 后序打印所有节点,递归版
	public static void posOrder(TreeNode head) {
		if (head == null) {
			return;
		}
		posOrder(head.left);
		posOrder(head.right);
		System.out.print(head.val + " ");
	}

	public static void main(String[] args) {
		TreeNode head = new TreeNode(1);
		head.left = new TreeNode(2);
		head.right = new TreeNode(3);
		head.left.left = new TreeNode(4);
		head.left.right = new TreeNode(5);
		head.right.left = new TreeNode(6);
		head.right.right = new TreeNode(7);

		preOrder(head);
		System.out.println();
		System.out.println("先序遍历递归版");

		inOrder(head);
		System.out.println();
		System.out.println("中序遍历递归版");

		posOrder(head);
		System.out.println();
		System.out.println("后序遍历递归版");

	}

}

18. (入门)二叉树及其三种序的非递归实现

package class018;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

// 不用递归,用迭代的方式实现二叉树的三序遍历
public class BinaryTreeTraversalIteration {

	public static class TreeNode {
		public int val;
		public TreeNode left;
		public TreeNode right;

		public TreeNode(int v) {
			val = v;
		}
	}

	// 先序打印所有节点,非递归版
	public static void preOrder(TreeNode head) {
		if (head != null) {
			Stack<TreeNode> stack = new Stack<>();
			stack.push(head);
			while (!stack.isEmpty()) {
				head = stack.pop();
				System.out.print(head.val + " ");
				if (head.right != null) {
					stack.push(head.right);
				}
				if (head.left != null) {
					stack.push(head.left);
				}
			}
			System.out.println();
		}
	}

	// 中序打印所有节点,非递归版
	public static void inOrder(TreeNode head) {
		if (head != null) {
			Stack<TreeNode> stack = new Stack<>();
			while (!stack.isEmpty() || head != null) {
				if (head != null) {
					stack.push(head);
					head = head.left;
				} else {
					head = stack.pop();
					System.out.print(head.val + " ");
					head = head.right;
				}
			}
			System.out.println();
		}
	}

	// 后序打印所有节点,非递归版
	// 这是用两个栈的方法
	public static void posOrderTwoStacks(TreeNode head) {
		if (head != null) {
			Stack<TreeNode> stack = new Stack<>();
			Stack<TreeNode> collect = new Stack<>();
			stack.push(head);
			while (!stack.isEmpty()) {
				head = stack.pop();
				collect.push(head);
				if (head.left != null) {
					stack.push(head.left);
				}
				if (head.right != null) {
					stack.push(head.right);
				}
			}
			while (!collect.isEmpty()) {
				System.out.print(collect.pop().val + " ");
			}
			System.out.println();
		}
	}

	// 后序打印所有节点,非递归版
	// 这是用一个栈的方法
	public static void posOrderOneStack(TreeNode h) {
		if (h != null) {
			Stack<TreeNode> stack = new Stack<>();
			stack.push(h);
			// 如果始终没有打印过节点,h就一直是头节点
			// 一旦打印过节点,h就变成打印节点
			// 之后h的含义 : 上一次打印的节点
			while (!stack.isEmpty()) {
				TreeNode cur = stack.peek();
				if (cur.left != null && h != cur.left && h != cur.right) {
					// 有左树且左树没处理过
					stack.push(cur.left);
				} else if (cur.right != null && h != cur.right) {
					// 有右树且右树没处理过
					stack.push(cur.right);
				} else {
					// 左树、右树 没有 或者 都处理过了
					System.out.print(cur.val + " ");
					h = stack.pop();
				}
			}
			System.out.println();
		}
	}

	public static void main(String[] args) {
		TreeNode head = new TreeNode(1);
		head.left = new TreeNode(2);
		head.right = new TreeNode(3);
		head.left.left = new TreeNode(4);
		head.left.right = new TreeNode(5);
		head.right.left = new TreeNode(6);
		head.right.right = new TreeNode(7);
		preOrder(head);
		System.out.println("先序遍历非递归版");
		inOrder(head);
		System.out.println("中序遍历非递归版");
		posOrderTwoStacks(head);
		System.out.println("后序遍历非递归版 - 2个栈实现");
		posOrderOneStack(head);
		System.out.println("后序遍历非递归版 - 1个栈实现");
	}

	// 用一个栈完成先序遍历
	// 测试链接 : https://leetcode.cn/problems/binary-tree-preorder-traversal/
	public static List<Integer> preorderTraversal(TreeNode head) {
		List<Integer> ans = new ArrayList<>();
		if (head != null) {
			Stack<TreeNode> stack = new Stack<>();
			stack.push(head);
			while (!stack.isEmpty()) {
				head = stack.pop();
				ans.add(head.val);
				if (head.right != null) {
					stack.push(head.right);
				}
				if (head.left != null) {
					stack.push(head.left);
				}
			}
		}
		return ans;
	}

	// 用一个栈完成中序遍历
	// 测试链接 : https://leetcode.cn/problems/binary-tree-inorder-traversal/
	public static List<Integer> inorderTraversal(TreeNode head) {
		List<Integer> ans = new ArrayList<>();
		if (head != null) {
			Stack<TreeNode> stack = new Stack<>();
			while (!stack.isEmpty() || head != null) {
				if (head != null) {
					stack.push(head);
					head = head.left;
				} else {
					head = stack.pop();
					ans.add(head.val);
					head = head.right;
				}
			}
		}
		return ans;
	}

	// 用两个栈完成后序遍历
	// 提交时函数名改为postorderTraversal
	// 测试链接 : https://leetcode.cn/problems/binary-tree-postorder-traversal/
	public static List<Integer> postorderTraversalTwoStacks(TreeNode head) {
		List<Integer> ans = new ArrayList<>();
		if (head != null) {
			Stack<TreeNode> stack = new Stack<>();
			Stack<TreeNode> collect = new Stack<>();
			stack.push(head);
			while (!stack.isEmpty()) {
				head = stack.pop();
				collect.push(head);
				if (head.left != null) {
					stack.push(head.left);
				}
				if (head.right != null) {
					stack.push(head.right);
				}
			}
			while (!collect.isEmpty()) {
				ans.add(collect.pop().val);
			}
		}
		return ans;
	}

	// 用一个栈完成后序遍历
	// 提交时函数名改为postorderTraversal
	// 测试链接 : https://leetcode.cn/problems/binary-tree-postorder-traversal/
	public static List<Integer> postorderTraversalOneStack(TreeNode h) {
		List<Integer> ans = new ArrayList<>();
		if (h != null) {
			Stack<TreeNode> stack = new Stack<>();
			stack.push(h);
			while (!stack.isEmpty()) {
				TreeNode cur = stack.peek();
				if (cur.left != null && h != cur.left && h != cur.right) {
					stack.push(cur.left);
				} else if (cur.right != null && h != cur.right) {
					stack.push(cur.right);
				} else {
					ans.add(cur.val);
					h = stack.pop();
				}
			}
		}
		return ans;
	}

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值