力扣刷题日记

排序算法

冒泡排序

 public static void bubbleSort(int[] arr){
        int temp=0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length-1-i;j++)
                if (arr[j]>arr[j+1]){
                   temp=arr[j];
                   arr[j]=arr[j+1];
                   arr[j+1]=temp;
            }
        }
    }

选择排序

选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]arr[n-1]中选取最小值,与arr[i-1]交换,…, 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

public static void selectSort(int[] arr) {
		//选择排序时间复杂度是 O(n^2)
		for (int i = 0; i < arr.length - 1; i++) {
			int minIndex = i;
			int min = arr[i];
			for (int j = i + 1; j < arr.length; j++) {
				if (min > arr[j]) { // 说明假定的最小值,并不是最小
					min = arr[j]; // 重置min
					minIndex = j; // 重置minIndex
				}
			}
			// 将最小值,放在arr[0], 即交换
			if (minIndex != i) {
				arr[minIndex] = arr[i];
				arr[i] = min;
			}

			//System.out.println("第"+(i+1)+"轮后~~");
			//System.out.println(Arrays.toString(arr));// 1, 34, 119, 101
		}

插入排序

插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

public static void insertSort(int[] arr) {
		int insertVal = 0;
		int insertIndex = 0;
		//使用for循环来把代码简化
		for(int i = 1; i < arr.length; i++) {
			//定义待插入的数
			insertVal = arr[i];
			insertIndex = i - 1; // 即arr[1]的前面这个数的下标
			// 给insertVal 找到插入的位置
			// 说明
			// 1. insertIndex >= 0 保证在给insertVal 找插入位置,不越界
			// 2. insertVal < arr[insertIndex] 待插入的数,还没有找到插入位置
			// 3. 就需要将 arr[insertIndex] 后移
			while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
				arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex]
				insertIndex--;
			}
			// 当退出while循环时,说明插入的位置找到, insertIndex + 1
			if(insertIndex + 1 != i) {
				arr[insertIndex + 1] = insertVal;
			}
		}

希尔排序

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

希尔排序法基本思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

//希尔排序法基本思想

//希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
public static void shellSort(int[] arr) {
	
		int temp = 0;
		int count = 0;
		// 根据前面的逐步分析,使用循环处理
    //gap为分组个数,当gap为1时进行最后一次排序
		for (int gap = arr.length / 2; gap > 0; gap /= 2) {
			for (int i = gap; i < arr.length; i++) {
				// 遍历各组中所有的元素(共gap组,每组有个元素), 步长gap
				for (int j = i - gap; j >= 0; j -= gap) {
					// 如果当前元素大于加上步长后的那个元素,说明交换
					if (arr[j] > arr[j + gap]) {
						temp = arr[j];
						arr[j] = arr[j + gap];
						arr[j + gap] = temp;
					}
				}
			}
		
		}

快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

//快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
	public static void quickSort(int[] arr,int left, int right) {
		int l = left; //左下标
		int r = right; //右下标
		//pivot 中轴值
		int pivot = arr[(left + right) / 2];
		int temp = 0; //临时变量,作为交换时使用
		//while循环的目的是让比pivot 值小放到左边
		//比pivot 值大放到右边
		while( l < r) { 
			//在pivot的左边一直找,找到大于等于pivot值,才退出
			while( arr[l] < pivot) {
				l += 1;
			}
			//在pivot的右边一直找,找到小于等于pivot值,才退出
			while(arr[r] > pivot) {
				r -= 1;
			}
			//如果l >= r说明pivot 的左右两的值,已经按照左边全部是
			//小于等于pivot值,右边全部是大于等于pivot值
			if( l >= r) {
				break;
			}	
			//交换
			temp = arr[l];
			arr[l] = arr[r];
			arr[r] = temp;
			
			//如果交换完后,发现这个arr[l] == pivot值 相等 r--, 前移
			if(arr[l] == pivot) {
				r -= 1;
			}
			//如果交换完后,发现这个arr[r] == pivot值 相等 l++, 后移
			if(arr[r] == pivot) {
				l += 1;
			}
		}
		// 如果 l == r, 必须l++, r--, 否则为出现栈溢出
		if (l == r) {
			l += 1;
			r -= 1;
		}
		//向左递归
		if(left < r) {
			quickSort(arr, left, r);
		}
		//向右递归
		if(right > l) {
			quickSort(arr, l, right);
		}
	}

并归排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)

	public static void mergeSort(int[] arr, int left, int right, int[] temp) {
		if(left < right) {
			int mid = (left + right) / 2; //中间索引
			//向左递归进行分解
			mergeSort(arr, left, mid, temp);
			//向右递归进行分解
			mergeSort(arr, mid + 1, right, temp);
			//合并
			merge(arr, left, mid, right, temp);
		}
	}
	//合并的方法
	/**
	 * 
	 * @param arr 排序的原始数组
	 * @param left 左边有序序列的初始索引
	 * @param mid 中间索引
	 * @param right 右边索引
	 * @param temp 做中转的数组
	 */
	public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
		
		int i = left; // 初始化i, 左边有序序列的初始索引
		int j = mid + 1; //初始化j, 右边有序序列的初始索引
		int t = 0; // 指向temp数组的当前索引
		
		//(一)
		//先把左右两边(有序)的数据按照规则填充到temp数组
		//直到左右两边的有序序列,有一边处理完毕为止
		while (i <= mid && j <= right) {//继续
			//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
			//即将左边的当前元素,填充到 temp数组 
			//然后 t++, i++
			if(arr[i] <= arr[j]) {
				temp[t] = arr[i];
				t += 1;
				i += 1;
			} else { //反之,将右边有序序列的当前元素,填充到temp数组
				temp[t] = arr[j];
				t += 1;
				j += 1;
			}
		}
		
		//(二)
		//把有剩余数据的一边的数据依次全部填充到temp
		while( i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到temp
			temp[t] = arr[i];
			t += 1;
			i += 1;	
		}
		
		while( j <= right) { //右边的有序序列还有剩余的元素,就全部填充到temp
			temp[t] = arr[j];
			t += 1;
			j += 1;	
		}
		//(三)
		//将temp数组的元素拷贝到arr
		//注意,并不是每次都拷贝所有
		t = 0;
		int tempLeft = left; // 
		//第一次合并 tempLeft = 0 , right = 1 //  tempLeft = 2  right = 3 // tL=0 ri=3
		//最后一次 tempLeft = 0  right = 7
		while(tempLeft <= right) { 
			arr[tempLeft] = temp[t];
			t += 1;
			tempLeft += 1;
		}
  

基数排序

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

基数排序(Radix Sort)是桶排序的扩展

基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

public static void radixSort(int[] arr) {
		
		//根据前面的推导过程,我们可以得到最终的基数排序代码
		
		//1. 得到数组中最大的数的位数
		int max = arr[0]; //假设第一数就是最大数
		for(int i = 1; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		//得到最大数是几位数
		int maxLength = (max + "").length();
		
		
		//定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
		//说明
		//1. 二维数组包含10个一维数组
		//2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
		//3. 名明确,基数排序是使用空间换时间的经典算法
		int[][] bucket = new int[10][arr.length];
		//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
		int[] bucketElementCounts = new int[10];
		
		for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
			//(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位..
			for(int j = 0; j < arr.length; j++) {
				//取出每个元素的对应位的值
				int digitOfElement = arr[j] / n % 10;
				//放入到对应的桶中
				bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
				bucketElementCounts[digitOfElement]++;
			}
			//按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
			int index = 0;
			//遍历每一桶,并将桶中是数据,放入到原数组
			for(int k = 0; k < bucketElementCounts.length; k++) {
				//如果桶中,有数据,我们才放入到原数组
				if(bucketElementCounts[k] != 0) {
					//循环该桶即第k个桶(即第k个一维数组), 放入
					for(int l = 0; l < bucketElementCounts[k]; l++) {
						//取出元素放入到arr
						arr[index++] = bucket[k][l];
					}
				}
				//第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
				bucketElementCounts[k] = 0;
			}			
		}

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆,

大顶堆特点:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] // i 对应第几个节点,i从0开始编号。注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。

每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

堆排序的基本思想是:
将待排序序列构造成一个大顶堆
此时,整个序列的最大值就是堆顶的根节点。
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

public static void heapSort(int arr[]) {
		int temp = 0;
		System.out.println("堆排序!!");
		

		
		//先从第一个非叶子节点开始调整,调整成大顶堆
		//将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
		for(int i = arr.length / 2 -1; i >=0; i--) {
			adjustHeap(arr, i, arr.length);
		}
		
		/*
		 * 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
  			3).重新调整结构,从第一个位置开始,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
		 */
		for(int j = arr.length-1;j >0; j--) {
			//交换
			temp = arr[j];
			arr[j] = arr[0];
			arr[0] = temp;
			adjustHeap(arr, 0, j); 
		}	
	}
	//将一个数组(二叉树), 调整成一个大顶堆
	/**
	 * 功能: 完成 将 以 i 对应的非叶子结点的树调整成大顶堆
	 * @param arr 待调整的数组
	 * @param i 表示非叶子结点在数组中索引
	 * @param lenght 表示对多少个元素继续调整, length 是在逐渐的减少
	 */
	public  static void adjustHeap(int arr[], int i, int lenght) {
		
		int temp = arr[i];//先取出当前元素的值,保存在临时变量
		//开始调整
		//说明
		//1. k = i * 2 + 1 k 是 i结点的左子结点
		for(int k = i * 2 + 1; k < lenght; k = k * 2 + 1) {
			if(k+1 < lenght && arr[k] < arr[k+1]) { //说明左子结点的值小于右子结点的值
				k++; // k 指向右子结点
			}
			if(arr[k] > temp) { //如果子结点大于父结点
				arr[i] = arr[k]; //把较大的值赋给当前结点
				i = k; //!!! i 指向 k,继续循环比较
			} else {
				break;//!
			}
		}
		//当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
		arr[i] = temp;//将temp值放到调整后的位置
	}
	

查找算法

二分查找

public static int binarySearch(int[] arr, int left, int right, int findVal) {
		// 当 left > right 时,说明递归整个数组,但是没有找到
		if (left > right) {
			return -1;
		}
		int mid = (left + right) / 2;
		int midVal = arr[mid];
		if (findVal > midVal) { // 向 右递归
			return binarySearch(arr, mid + 1, right, findVal);
		} else if (findVal < midVal) { // 向左递归
			return binarySearch(arr, left, mid - 1, findVal);
		} else {
			return mid;
		}

	}

差值查找

插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找
   	public static int insertValueSearch(int[] arr, int left, int right, int findVal) { 
		//注意:findVal < arr[0]  和  findVal > arr[arr.length - 1] 必须需要
		//否则我们得到的 mid 可能越界
		if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
			return -1;
		}
		// 求出mid, 自适应
   //的越多,增量越大
		int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
		int midVal = arr[mid];
		if (findVal > midVal) { // 说明应该向右边递归
			return insertValueSearch(arr, mid + 1, right, findVal);
		} else if (findVal < midVal) { // 说明向左递归查找
			return insertValueSearch(arr, left, mid - 1, findVal);
		} else {
			return mid;
		}
	} 

斐波那契查找

public static int[] fib(int maxSize) {
		int[] f = new int[maxSize];
		f[0] = 1;
		f[1] = 1;
		for (int i = 2; i < maxSize; i++) {
			f[i] = f[i - 1] + f[i - 2];
		}
		return f;
	}
	
	//编写斐波那契查找算法
	//使用非递归的方式编写算法
	/**
	 * 
	 * @param a  数组
	 * @param key 我们需要查找的关键码(值)
	 * @return 返回对应的下标,如果没有-1
	 */
	public static int fibSearch(int[] a, int key) {
		int low = 0;
		int high = a.length - 1;
		int k = 0; //表示斐波那契分割数值的下标
		int mid = 0; //存放mid值
		int f[] = fib(maxSize); //获取到斐波那契数列
		//获取到斐波那契分割数值的下标
		while(high > f[k] - 1) {
			k++;
		}
		//因为 f[k] 值 可能大于 a 的 长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp[]
		//不足的部分会使用0填充
		int[] temp = Arrays.copyOf(a, f[k]);
		//实际上需求使用a数组最后的数填充 temp
		//举例:
		//temp = {1,8, 10, 89, 1000, 1234, 0, 0}  => {1,8, 10, 89, 1000, 1234, 1234, 1234,}
		for(int i = high + 1; i < temp.length; i++) {
			temp[i] = a[high];
		}
		
		// 使用while来循环处理,找到我们的数 key
		while (low <= high) { // 只要这个条件满足,就可以找
			mid = low + f[k - 1] - 1;
			if(key < temp[mid]) { //我们应该继续向数组的前面查找(左边)
				high = mid - 1;
				//为甚是 k--
				//说明
				//1. 全部元素 = 前面的元素 + 后边元素
				//2. f[k] = f[k-1] + f[k-2]
				//因为 前面有 f[k-1]个元素,所以可以继续拆分 f[k-1] = f[k-2] + f[k-3]
				//即 在 f[k-1] 的前面继续查找 k--
				//即下次循环 mid = f[k-1-1]-1
				k--;
			} else if ( key > temp[mid]) { // 我们应该继续向数组的后面查找(右边)
				low = mid + 1;
				//为什么是k -=2
				//说明
				//1. 全部元素 = 前面的元素 + 后边元素
				//2. f[k] = f[k-1] + f[k-2]
				//3. 因为后面我们有f[k-2] 所以可以继续拆分 f[k-1] = f[k-3] + f[k-4]
				//4. 即在f[k-2] 的前面进行查找 k -=2
				//5. 即下次循环 mid = f[k - 1 - 2] - 1
				k -= 2;
			} else { //找到
				//需要确定,返回的是哪个下标
				if(mid <= high) {
					return mid;
				} else {
					return high;
				}
			}
		}
		return -1;
	}

二叉树

二叉树遍历

class HeroNode {
	private int no;
	private String name;
	private HeroNode left; //默认null
	private HeroNode right; //默认null
	public HeroNode(int no, String name) {
		this.no = no;
		this.name = name;
	}
public void preOrder() {
		System.out.println(this); //先输出父结点
		//递归向左子树前序遍历
		if(this.left != null) {
			this.left.preOrder();
		}
		//递归向右子树前序遍历
		if(this.right != null) {
			this.right.preOrder();
		}
	}
	//中序遍历
	public void infixOrder() {
		
		//递归向左子树中序遍历
		if(this.left != null) {
			this.left.infixOrder();
		}
		//输出父结点
		System.out.println(this);
		//递归向右子树中序遍历
		if(this.right != null) {
			this.right.infixOrder();
		}
	}
	//后序遍历
	public void postOrder() {
		if(this.left != null) {
			this.left.postOrder();
		}
		if(this.right != null) {
			this.right.postOrder();
		}
		System.out.println(this);
	}
    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
		//比较当前结点是不是
		if(this.no == no) {
			return this;
		}
		//1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
		//2.如果左递归前序查找,找到结点,则返回
		HeroNode resNode = null;
		if(this.left != null) {
			resNode = this.left.preOrderSearch(no);
		}
		if(resNode != null) {//说明我们左子树找到
			return resNode;
		}
		//1.左递归前序查找,找到结点,则返回,否继续判断,
		//2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找
		if(this.right != null) {
			resNode = this.right.preOrderSearch(no);
		}
		return resNode;
	}
   
	}

删除节点

	public void delNode(int no) {	
		//思路
		/*
		 * 	1. 因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点.
			2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
			3. 如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
			4. 如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除
			5.  如果第4步也没有删除结点,则应当向右子树进行递归删除.
		 */
		//2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
		if(this.left != null && this.left.no == no) {
			this.left = null;
			return;
		}
		//3.如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
		if(this.right != null && this.right.no == no) {
			this.right = null;
			return;
		}
		//4.我们就需要向左子树进行递归删除
		if(this.left != null) {
			this.left.delNode(no);
		}
		//5.则应当向右子树进行递归删除
		if(this.right != null) {
			this.right.delNode(no);
		}
	}

顺序存储二叉树

顺序二叉树通常只考虑完全二叉树
第n个元素的左子节点为 2 * n + 1
第n个元素的右子节点为 2 * n + 2
第n个元素的父节点为 (n-1) / 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWGX8ZPZ-1657510180973)(D:\学习资料\img\1656551363202.png)]

class ArrBinaryTree {
	private int[] arr;//存储数据结点的数组
/**
给你一个数组 {1,2,3,4,5,6,7},要求以二叉树前序遍历的方式进行遍历。 前序遍历的结果应当为 1,2,4,5,3,6,7
*/
	public ArrBinaryTree(int[] arr) {
		this.arr = arr;
	}
	
	//重载preOrder
	public void preOrder() {
		this.preOrder(0);
	}
	
	//编写一个方法,完成顺序存储二叉树的前序遍历
	/**
	 * 
	 * @param index 数组的下标 
	 */

顺序储蓄二叉树的前序遍历

	public void preOrder(int index) {
		//如果数组为空,或者 arr.length = 0
		if(arr == null || arr.length == 0) {
			System.out.println("数组为空,不能按照二叉树的前序遍历");
		}
		//输出当前这个元素
		System.out.println(arr[index]); 
		//向左递归遍历
		if((index * 2 + 1) < arr.length) {
			preOrder(2 * index + 1 );
		}
		//向右递归遍历
		if((index * 2 + 2) < arr.length) {
			preOrder(2 * index + 2);
		}
	}
	
}

线索化二叉树

class ThreadedBinaryTree {
	private HeroNode root;
	
	//为了实现线索化,需要创建要给指向当前结点的前驱结点的指针
	//在递归进行线索化时,pre 总是保留前一个结点
	private HeroNode pre = null;

	public void setRoot(HeroNode root) {
		this.root = root;
	}
	
	//重载一把threadedNodes方法
	public void threadedNodes() {
		this.threadedNodes(root);
	}
	//前序遍历查找
	public HeroNode preOrderSearch(int no) {
		if(root != null) {
			return root.preOrderSearch(no);
		} else {
			return null;
		}
	}
	//中序遍历查找
	public HeroNode infixOrderSearch(int no) {
		if(root != null) {
			return root.infixOrderSearch(no);
		}else {
			return null;
		}
	}
	//后序遍历查找
	public HeroNode postOrderSearch(int no) {
		if(root != null) {
			return this.root.postOrderSearch(no);
		}else {
			return null;
		}
	}
}
	public void preOrder() {
		if(this.root != null) {
			this.root.preOrder();
		}else {
			System.out.println("二叉树为空,无法遍历");
		}
	}
	
	//中序遍历
	public void infixOrder() {
		if(this.root != null) {
			this.root.infixOrder();
		}else {
			System.out.println("二叉树为空,无法遍历");
		}
	}

中序线索化二叉树

	
	//编写对二叉树进行中序线索化的方法
	/**
	 * 
	 * @param node 就是当前需要线索化的结点
	 */
	public void threadedNodes(HeroNode node) {	
		//如果node==null, 不能线索化
		if(node == null) {
			return;
		}
		//(一)先线索化左子树
		threadedNodes(node.getLeft());
		//(二)线索化当前结点[有难度]
		//处理当前结点的前驱结点
		//以8结点来理解
		//8结点的.left = null , 8结点的.leftType = 1
		if(node.getLeft() == null) {
			//让当前结点的左指针指向前驱结点 
			node.setLeft(pre); 
			//修改当前结点的左指针的类型,指向前驱结点
			node.setLeftType(1);
		}
		//处理后继结点
		if (pre != null && pre.getRight() == null) {
			//让前驱结点的右指针指向当前结点
			pre.setRight(node);
			//修改前驱结点的右指针类型
			pre.setRightType(1);
		}
		//!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点
		pre = node;
		//(三)在线索化右子树
		threadedNodes(node.getRight());
	}

中序线索化二叉树遍历

	//遍历线索化二叉树的方法
	public void threadedList() {
		//定义一个变量,存储当前遍历的结点,从root开始
		HeroNode node = root;
		while(node != null) {
			//循环的找到leftType == 1的结点,第一个找到就是8结点
			//后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
			//处理后的有效结点
			while(node.getLeftType() == 0) {
				node = node.getLeft();
			}
			
			//打印当前这个结点
			System.out.println(node);
			//如果当前结点的右指针指向的是后继结点,就一直输出
			while(node.getRightType() == 1) {
				//获取到当前结点的后继结点
				node = node.getRight();
				System.out.println(node);
			}
			//替换这个遍历的结点
			node = node.getRight();
			
		}
	}

class HeroNode {
	private int no;
	private String name;
	private HeroNode left; //默认null
	private HeroNode right; //默认null
	//说明
	//1. 如果leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点
	//2. 如果rightType == 0 表示指向是右子树, 如果 1表示指向后继结点
	private int leftType;
	private int rightType;
	
	
	
	public int getLeftType() {
		return leftType;
	}
	public void setLeftType(int leftType) {
		this.leftType = leftType;
	}
	public int getRightType() {
		return rightType;
	}
	public void setRightType(int rightType) {
		this.rightType = rightType;
	}
	public HeroNode(int no, String name) {
		this.no = no;
		this.name = name;
	}
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public HeroNode getLeft() {
		return left;
	}
	public void setLeft(HeroNode left) {
		this.left = left;
	}
	public HeroNode getRight() {
		return right;
	}
	public void setRight(HeroNode right) {
		this.right = right;
	}
	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + "]";
	}
	
	//递归删除结点
	//1.如果删除的节点是叶子节点,则删除该节点
	//2.如果删除的节点是非叶子节点,则删除该子树
	public void delNode(int no) {	
		//思路
		/*
		 * 	1. 因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点.
			2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
			3. 如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
			4. 如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除
			5.  如果第4步也没有删除结点,则应当向右子树进行递归删除.

		 */
		//2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
		if(this.left != null && this.left.no == no) {
			this.left = null;
			return;
		}
		//3.如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
		if(this.right != null && this.right.no == no) {
			this.right = null;
			return;
		}
		//4.我们就需要向左子树进行递归删除
		if(this.left != null) {
			this.left.delNode(no);
		}
		//5.则应当向右子树进行递归删除
		if(this.right != null) {
			this.right.delNode(no);
		}
	}
	
	//编写前序遍历的方法
	public void preOrder() {
		System.out.println(this); //先输出父结点
		//递归向左子树前序遍历
		if(this.left != null) {
			this.left.preOrder();
		}
		//递归向右子树前序遍历
		if(this.right != null) {
			this.right.preOrder();
		}
	}
	//中序遍历
	public void infixOrder() {
		
		//递归向左子树中序遍历
		if(this.left != null) {
			this.left.infixOrder();
		}
		//输出父结点
		System.out.println(this);
		//递归向右子树中序遍历
		if(this.right != null) {
			this.right.infixOrder();
		}
	}
	//后序遍历
	public void postOrder() {
		if(this.left != null) {
			this.left.postOrder();
		}
		if(this.right != null) {
			this.right.postOrder();
		}
		System.out.println(this);
	}
	
	//前序遍历查找
	/**
	 * 
	 * @param no 查找no
	 * @return 如果找到就返回该Node ,如果没有找到返回 null
	 */
	public HeroNode preOrderSearch(int no) {
		System.out.println("进入前序遍历");
		//比较当前结点是不是
		if(this.no == no) {
			return this;
		}
		//1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
		//2.如果左递归前序查找,找到结点,则返回
		HeroNode resNode = null;
		if(this.left != null) {
			resNode = this.left.preOrderSearch(no);
		}
		if(resNode != null) {//说明我们左子树找到
			return resNode;
		}
		//1.左递归前序查找,找到结点,则返回,否继续判断,
		//2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找
		if(this.right != null) {
			resNode = this.right.preOrderSearch(no);
		}
		return resNode;
	}
	
	//中序遍历查找
	public HeroNode infixOrderSearch(int no) {
		//判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
		HeroNode resNode = null;
		if(this.left != null) {
			resNode = this.left.infixOrderSearch(no);
		}
		if(resNode != null) {
			return resNode;
		}
		System.out.println("进入中序查找");
		//如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点
		if(this.no == no) {
			return this;
		}
		//否则继续进行右递归的中序查找
		if(this.right != null) {
			resNode = this.right.infixOrderSearch(no);
		}
		return resNode;
		
	}
	
	//后序遍历查找
	public HeroNode postOrderSearch(int no) {
		
		//判断当前结点的左子节点是否为空,如果不为空,则递归后序查找
		HeroNode resNode = null;
		if(this.left != null) {
			resNode = this.left.postOrderSearch(no);
		}
		if(resNode != null) {//说明在左子树找到
			return resNode;
		}
		
		//如果左子树没有找到,则向右子树递归进行后序遍历查找
		if(this.right != null) {
			resNode = this.right.postOrderSearch(no);
		}
		if(resNode != null) {
			return resNode;
		}
		System.out.println("进入后序查找");
		//如果左右子树都没有找到,就比较当前结点是不是
		if(this.no == no) {
			return this;
		}
		return resNode;
	}

二叉排序树

二叉排序树:BST: (Binary Sort(Search) Tree), 对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点

节点代码

class Node {
	int value;
	Node left;
	Node right;
	public Node(int value) {
		
		this.value = value;
	}
	//查找要删除的结点
	/**
	 * 
	 * @param value 希望删除的结点的值
	 * @return 如果找到返回该结点,否则返回null
	 */
	public Node search(int value) {
		if(value == this.value) { //找到就是该结点
			return this;
		} else if(value < this.value) {//如果查找的值小于当前结点,向左子树递归查找
			//如果左子结点为空
			if(this.left  == null) {
				return null;
			}
			return this.left.search(value);
		} else { //如果查找的值不小于当前结点,向右子树递归查找
			if(this.right == null) {
				return null;
			}
			return this.right.search(value);
		}	
	}
	//查找要删除结点的父结点
	/**
	 * 
	 * @param value 要找到的结点的值
	 * @return 返回的是要删除的结点的父结点,如果没有就返回null
	 */
	public Node searchParent(int value) {
		//如果当前结点就是要删除的结点的父结点,就返回
		if((this.left != null && this.left.value == value) || 
				(this.right != null && this.right.value == value)) {
			return this;
		} else {
			//如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
			if(value < this.value && this.left != null) {
				return this.left.searchParent(value); //向左子树递归查找
			} else if (value >= this.value && this.right != null) {
				return this.right.searchParent(value); //向右子树递归查找
			} else {
				return null; // 没有找到父结点
			}
		}
		
	}
	
	//添加结点的方法
	//递归的形式添加结点,注意需要满足二叉排序树的要求
	public void add(Node node) {
		if(node == null) {
			return;
		}
		//判断传入的结点的值,和当前子树的根结点的值关系
		if(node.value < this.value) {
			//如果当前结点左子结点为null
			if(this.left == null) {
				this.left = node;
			} else {
				//递归的向左子树添加
				this.left.add(node);
			}
		} else { //添加的结点的值大于 当前结点的值
			if(this.right == null) {
				this.right = node;
			} else {
				//递归的向右子树添加
				this.right.add(node);
			}
			
		}
	}
	
	//中序遍历
	public void infixOrder() {
		if(this.left != null) {
			this.left.infixOrder();
		}
		System.out.println(this);
		if(this.right != null) {
			this.right.infixOrder();
		}
	}
}
class BinarySortTree {
	private Node root;
	
	public Node getRoot() {
		return root;
	}

	//查找要删除的结点
	public Node search(int value) {
		if(root == null) {
			return null;
		} else {
			return root.search(value);
		}
	}
	
	//查找父结点
	public Node searchParent(int value) {
		if(root == null) {
			return null;
		} else {
			return root.searchParent(value);
		}
	}
	//编写方法: 
	//1. 返回的 以node 为根结点的二叉排序树的最小结点的值
	//2. 删除node 为根结点的二叉排序树的最小结点
	/**
	 * 
	 * @param node 传入的结点(当做二叉排序树的根结点)
	 * @return 返回的 以node 为根结点的二叉排序树的最小结点的值
	 */
	public int delRightTreeMin(Node node) {
		Node target = node;
		//循环的查找左子节点,就会找到最小值
		while(target.left != null) {
			target = target.left;
		}
		//这时 target就指向了最小结点
		//删除最小结点
		delNode(target.value);
		return target.value;
	}
	
	//删除结点
	public void delNode(int value) {
		if(root == null) {
			return;
		}else {
			//1.需求先去找到要删除的结点  targetNode
			Node targetNode = search(value);
			//如果没有找到要删除的结点
			if(targetNode == null) {
				return;
			}
			//如果我们发现当前这颗二叉排序树只有一个结点
			if(root.left == null && root.right == null) {
				root = null;
				return;
			}
			//去找到targetNode的父结点
			Node parent = searchParent(value);
			//如果要删除的结点是叶子结点
			if(targetNode.left == null && targetNode.right == null) {
				//判断targetNode 是父结点的左子结点,还是右子结点
				if(parent.left != null && parent.left.value == value) { //是左子结点
					parent.left = null;
				} else if (parent.right != null && parent.right.value == value) {//是右子结点
					parent.right = null;
				}
			} else if (targetNode.left != null && targetNode.right != null) { //删除有两颗子树的节点
				int minVal = delRightTreeMin(targetNode.right);
				targetNode.value = minVal;		
				
			} else { // 删除只有一颗子树的结点
				//如果要删除的结点有左子结点 
				if(targetNode.left != null) {
					if(parent != null) {
						//如果 targetNode 是 parent 的左子结点
						if(parent.left.value == value) {
							parent.left = targetNode.left;
						} else { //  targetNode 是 parent 的右子结点
							parent.right = targetNode.left;
						} 
					} else {
						root = targetNode.left;
					}
				} else { //如果要删除的结点有右子结点 
					if(parent != null) {
						//如果 targetNode 是 parent 的左子结点
						if(parent.left.value == value) {
							parent.left = targetNode.right;
						} else { //如果 targetNode 是 parent 的右子结点
							parent.right = targetNode.right;
						}
					} else {
						root = targetNode.right;
					}
				}
				
			}
			
		}
	}

平衡AVL树,平衡二叉排序树

给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vFD2OfNs-1657510180976)(D:\学习资料\img\1656553214080.png)]

平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。
具有以下特点:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。

Node节点

class Node {
	int value;
	Node left;
	Node right;

	public Node(int value) {

		this.value = value;
	}

	// 返回左子树的高度
	public int leftHeight() {
		if (left == null) {
			return 0;
		}
		return left.height();
	}

	// 返回右子树的高度
	public int rightHeight() {
		if (right == null) {
			return 0;
		}
		return right.height();
	}

	// 返回 以该结点为根结点的树的高度
	public int height() {
		return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
	}
	
	//左旋转方法
	private void leftRotate() {
		//创建新的结点,以当前根结点的值
		Node newNode = new Node(value);
		//把新的结点的左子树设置成当前结点的左子树
		newNode.left = left;
		//把新的结点的右子树设置成带你过去结点的右子树的左子树
		newNode.right = right.left;
		//把当前结点的值替换成右子结点的值
		value = right.value;
		//把当前结点的右子树设置成当前结点右子树的右子树
		right = right.right;
		//把当前结点的左子树(左子结点)设置成新的结点
		left = newNode;
	}
	
	//右旋转
	private void rightRotate() {
		Node newNode = new Node(value);
		newNode.right = right;
		newNode.left = left.right;
		value = left.value;
		left = left.left;
		right = newNode;
	}

	// 查找要删除的结点
	/**
	 * 
	 * @param value
	 *            希望删除的结点的值
	 * @return 如果找到返回该结点,否则返回null
	 */
	public Node search(int value) {
		if (value == this.value) { // 找到就是该结点
			return this;
		} else if (value < this.value) {// 如果查找的值小于当前结点,向左子树递归查找
			// 如果左子结点为空
			if (this.left == null) {
				return null;
			}
			return this.left.search(value);
		} else { // 如果查找的值不小于当前结点,向右子树递归查找
			if (this.right == null) {
				return null;
			}
			return this.right.search(value);
		}

	}

	// 查找要删除结点的父结点
	/**
	 * 
	 * @param value
	 *            要找到的结点的值
	 * @return 返回的是要删除的结点的父结点,如果没有就返回null
	 */
	public Node searchParent(int value) {
		// 如果当前结点就是要删除的结点的父结点,就返回
		if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
			return this;
		} else {
			// 如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
			if (value < this.value && this.left != null) {
				return this.left.searchParent(value); // 向左子树递归查找
			} else if (value >= this.value && this.right != null) {
				return this.right.searchParent(value); // 向右子树递归查找
			} else {
				return null; // 没有找到父结点
			}
		}

	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	// 添加结点的方法
	// 递归的形式添加结点,注意需要满足二叉排序树的要求
	public void add(Node node) {
		if (node == null) {
			return;
		}

		// 判断传入的结点的值,和当前子树的根结点的值关系
		if (node.value < this.value) {
			// 如果当前结点左子结点为null
			if (this.left == null) {
				this.left = node;
			} else {
				// 递归的向左子树添加
				this.left.add(node);
			}
		} else { // 添加的结点的值大于 当前结点的值
			if (this.right == null) {
				this.right = node;
			} else {
				// 递归的向右子树添加
				this.right.add(node);
			}
		}
		
		//当添加完一个结点后,如果: (右子树的高度-左子树的高度) > 1 , 左旋转
		if(rightHeight() - leftHeight() > 1) {
			//如果它的右子树的左子树的高度大于它的右子树的右子树的高度
			if(right != null && right.leftHeight() > right.rightHeight()) {
				//先对右子结点进行右旋转
				right.rightRotate();
				//然后在对当前结点进行左旋转
				leftRotate(); //左旋转..
			} else {
				//直接进行左旋转即可
				leftRotate();
			}
			return ; //必须要!!!
		}
		
		//当添加完一个结点后,如果 (左子树的高度 - 右子树的高度) > 1, 右旋转
		if(leftHeight() - rightHeight() > 1) {
			//如果它的左子树的右子树高度大于它的左子树的高度
			if(left != null && left.rightHeight() > left.leftHeight()) {
				//先对当前结点的左结点(左子树)->左旋转
				left.leftRotate();
				//再对当前结点进行右旋转
				rightRotate();
			} else {
				//直接进行右旋转即可
				rightRotate();
			}
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (this.left != null) {
			this.left.infixOrder();
		}
		System.out.println(this);
		if (this.right != null) {
			this.right.infixOrder();
		}
	}

}

AVl树

class AVLTree {
	private Node root;

	public Node getRoot() {
		return root;
	}

	// 查找要删除的结点
	public Node search(int value) {
		if (root == null) {
			return null;
		} else {
			return root.search(value);
		}
	}

	// 查找父结点
	public Node searchParent(int value) {
		if (root == null) {
			return null;
		} else {
			return root.searchParent(value);
		}
	}

	// 编写方法:
	// 1. 返回的 以node 为根结点的二叉排序树的最小结点的值
	// 2. 删除node 为根结点的二叉排序树的最小结点
	/**
	 * 
	 * @param node
	 *            传入的结点(当做二叉排序树的根结点)
	 * @return 返回的 以node 为根结点的二叉排序树的最小结点的值
	 */
	public int delRightTreeMin(Node node) {
		Node target = node;
		// 循环的查找左子节点,就会找到最小值
		while (target.left != null) {
			target = target.left;
		}
		// 这时 target就指向了最小结点
		// 删除最小结点
		delNode(target.value);
		return target.value;
	}

	// 删除结点
	public void delNode(int value) {
		if (root == null) {
			return;
		} else {
			// 1.需求先去找到要删除的结点 targetNode
			Node targetNode = search(value);
			// 如果没有找到要删除的结点
			if (targetNode == null) {
				return;
			}
			// 如果我们发现当前这颗二叉排序树只有一个结点
			if (root.left == null && root.right == null) {
				root = null;
				return;
			}

			// 去找到targetNode的父结点
			Node parent = searchParent(value);
			// 如果要删除的结点是叶子结点
			if (targetNode.left == null && targetNode.right == null) {
				// 判断targetNode 是父结点的左子结点,还是右子结点
				if (parent.left != null && parent.left.value == value) { // 是左子结点
					parent.left = null;
				} else if (parent.right != null && parent.right.value == value) {// 是由子结点
					parent.right = null;
				}
			} else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点
				int minVal = delRightTreeMin(targetNode.right);
				targetNode.value = minVal;

			} else { // 删除只有一颗子树的结点
				// 如果要删除的结点有左子结点
				if (targetNode.left != null) {
					if (parent != null) {
						// 如果 targetNode 是 parent 的左子结点
						if (parent.left.value == value) {
							parent.left = targetNode.left;
						} else { // targetNode 是 parent 的右子结点
							parent.right = targetNode.left;
						}
					} else {
						root = targetNode.left;
					}
				} else { // 如果要删除的结点有右子结点
					if (parent != null) {
						// 如果 targetNode 是 parent 的左子结点
						if (parent.left.value == value) {
							parent.left = targetNode.right;
						} else { // 如果 targetNode 是 parent 的右子结点
							parent.right = targetNode.right;
						}
					} else {
						root = targetNode.right;
					}
				}
			}
		}
	}

	// 添加结点的方法
	public void add(Node node) {
		if (root == null) {
			root = node;// 如果root为空则直接让root指向node
		} else {
			root.add(node);
		}
	}
	// 中序遍历
	public void infixOrder() {
		if (root != null) {
			root.infixOrder();
		} else {
			System.out.println("二叉排序树为空,不能遍历");
		}
	}
}

2-3树,B树,B+树

2-3树的所有叶子节点都在同一层.(只要是B树都满足这个条件)
有两个子节点的节点叫二节点,二节点要么没有子节点,要么有两个子节点.

有三个子节点的节点叫三节点,三节点要么没有子节点,要么有三个子节点. 2-3树是由二节点和三节点构成的树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AGhLZJbG-1657510180979)(C:\Users\Hao\AppData\Roaming\Typora\typora-user-images\1656554264265.png)]

B树的阶:节点的最多子节点个数。比如2-3树的阶是3,2-3-4树的阶是4
B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点

关键字集合分布在整颗树中, 即叶子节点和非叶子节点都存放数据.
搜索有可能在非叶子结点结束
其搜索性能等价于在关键字全集内做一次二分查找

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rM3cpb9r-1657510180980)(D:\学习资料\img\1656554351835.png)]

B+树是B树的变体,也是一种多路搜索树。B+树的搜索与B树也基本相同,区别是B+树只有达到叶子结点才命中(B树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找
所有关键字都出现在叶子结点的链表中(即数据只能在叶子节点【也叫稠密索引】),且链表中的关键字(数据)恰好是有序的。
不可能在非叶子结点命中
非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层
更适合文件索引系统
B树和B+树各有自己的应用场景,不能说B+树完全比B树好,反之亦然.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qJtC27l-1657510180981)(D:\学习资料\img\1656554391391.png)]

图的深度优先和广度优先

package com.atguigu.graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class Graph {

	private ArrayList<String> vertexList; //存储顶点集合
	private int[][] edges; //存储图对应的邻结矩阵
	private int numOfEdges; //表示边的数目
	//定义给数组boolean[], 记录某个结点是否被访问
	private boolean[] isVisited;
	//构造器
	public Graph(int n) {
		//初始化矩阵和vertexList
		edges = new int[n][n];
		vertexList = new ArrayList<String>(n);
		numOfEdges = 0;
		
	}
	
	//得到第一个邻接结点的下标 w 
	/**
	 * 
	 * @param index 
	 * @return 如果存在就返回对应的下标,否则返回-1
	 */
	public int getFirstNeighbor(int index) {
		for(int j = 0; j < vertexList.size(); j++) {
			if(edges[index][j] > 0) {
				return j;
			}
		}
		return -1;
	}
	//根据前一个邻接结点的下标来获取下一个邻接结点
	public int getNextNeighbor(int v1, int v2) {
		for(int j = v2 + 1; j < vertexList.size(); j++) {
			if(edges[v1][j] > 0) {
				return j;
			}
		}
		return -1;
	}
	
	//深度优先遍历算法
	//i 第一次就是 0
	private void dfs(boolean[] isVisited, int i) {
		//首先我们访问该结点,输出
		System.out.print(getValueByIndex(i) + "->");
		//将结点设置为已经访问
		isVisited[i] = true;
		//查找结点i的第一个邻接结点w
		int w = getFirstNeighbor(i);
		while(w != -1) {//说明有
			if(!isVisited[w]) {
				dfs(isVisited, w);
			}
			//如果w结点已经被访问过
			w = getNextNeighbor(i, w);
		}
		
	}
	
	//对dfs 进行一个重载, 遍历我们所有的结点,并进行 dfs
	public void dfs() {
		isVisited = new boolean[vertexList.size()];
		//遍历所有的结点,进行dfs[回溯]
		for(int i = 0; i < getNumOfVertex(); i++) {
			if(!isVisited[i]) {
				dfs(isVisited, i);
			}
		}
	}
	
	//对一个结点进行广度优先遍历的方法
	private void bfs(boolean[] isVisited, int i) {
		int u ; // 表示队列的头结点对应下标
		int w ; // 邻接结点w
		//队列,记录结点访问的顺序
		LinkedList queue = new LinkedList();
		//访问结点,输出结点信息
		System.out.print(getValueByIndex(i) + "=>");
		//标记为已访问
		isVisited[i] = true;
		//将结点加入队列
		queue.addLast(i);
		
		while( !queue.isEmpty()) {
			//取出队列的头结点下标
			u = (Integer)queue.removeFirst();
			//得到第一个邻接结点的下标 w 
			w = getFirstNeighbor(u);
			while(w != -1) {//找到
				//是否访问过
				if(!isVisited[w]) {
					System.out.print(getValueByIndex(w) + "=>");
					//标记已经访问
					isVisited[w] = true;
					//入队
					queue.addLast(w);
				}
				//以u为前驱点,找w后面的下一个邻结点
				w = getNextNeighbor(u, w); //体现出我们的广度优先
			}
		}
		
	} 
	
	//遍历所有的结点,都进行广度优先搜索
	public void bfs() {
		isVisited = new boolean[vertexList.size()];
		for(int i = 0; i < getNumOfVertex(); i++) {
			if(!isVisited[i]) {
				bfs(isVisited, i);
			}
		}
	}
	
	//图中常用的方法
	//返回结点的个数
	public int getNumOfVertex() {
		return vertexList.size();
	}
	//显示图对应的矩阵
	public void showGraph() {
		for(int[] link : edges) {
			System.err.println(Arrays.toString(link));
		}
	}
	//得到边的数目
	public int getNumOfEdges() {
		return numOfEdges;
	}
	//返回结点i(下标)对应的数据 0->"A" 1->"B" 2->"C"
	public String getValueByIndex(int i) {
		return vertexList.get(i);
	}
	//返回v1和v2的权值
	public int getWeight(int v1, int v2) {
		return edges[v1][v2];
	}
	//插入结点
	public void insertVertex(String vertex) {
		vertexList.add(vertex);
	}
	//添加边
	/**
	 * @param v1 表示点的下标即使第几个顶点  "A"-"B" "A"->0 "B"->1
	 * @param v2 第二个顶点对应的下标
	 * @param weight 表示 
	 */
	public void insertEdge(int v1, int v2, int weight) {
		edges[v1][v2] = weight;
		edges[v2][v1] = weight;
		numOfEdges++;
	}
}

计算系列

杨辉三角

给定一个非负整数 *numRows,*生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

class Solution {
        private List<List<Integer>> ans;
    private List<Integer> curans;
    public List<List<Integer>> generate(int numRows) {
       ans=new ArrayList<List<Integer>>();
        curans=new ArrayList<>();
        int[][] ansgrid =new int[numRows][numRows];
        for (int i = 0; i <ansgrid.length ; i++) {
            for (int j = 0; j <ansgrid[0].length ; j++) {
                if (j==0||i==j){
                    ansgrid[i][j]=1;
                    continue;
                }
               ansgrid[i][j]=0;
            }
            
        }
        for (int i = 2; i <ansgrid.length ; i++) {
            for (int j = 1; j <i ; j++) {
                ansgrid[i][j]=ansgrid[i-1][j-1]+ansgrid[i-1][j];
            }
        }
        for (int i = 0; i <ansgrid.length ; i++) {
            curans=new ArrayList<>();
            for (int j = 0; j <=i ; j++) {
              curans.add(ansgrid[i][j]);
            }
            ans.add(curans);
        }
        return ans;
    }
}

加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
class Solution {
    public int[] plusOne(int[] digits) {
   if (digits[digits.length-1]<=8){
            digits[digits.length-1]=digits[digits.length-1]+1;
            return digits;
        }
    if(digits.length==1){
        int[] ans = new int[digits.length+1];
                ans[0]=1;
                return ans;
    }
        digits[digits.length-1]=0;
        int count=1;
        for (int i = digits.length-2; i >=0 ; i--) {
            int curval=digits[i]+count;
            if (i==0&&curval==10){
                int[] ans = new int[digits.length+1];
                ans[0]=1;
                return ans;
            }
            if(curval==10){
                count=1;
                digits[i]=0;
                continue;
            }
            digits[i]=curval;
            break;
        }
        return digits;
    }
}

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

nums = [2,7,11,15], target = 9
输出:[0,1]
class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> hashtable = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (hashtable.containsKey(target - nums[i])) {
                int start = hashtable.get(target - nums[i]);
                int end = i;
              int ans[]={start,end};
              return ans;
            }
            hashtable.put(nums[i], i);
    }
    return new int[0];
}
}

两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        
       int plus = 0;
		int val = l1.val + l2.val;
		plus = val / 10;//进位
		ListNode rs = new ListNode(val % 10);
		ListNode p1 = l1.next, p2 = l2.next, cur = rs;
		while (p1 != null || p2 != null || plus != 0) {
			val = 0;
			if (p1 != null) {
				val += p1.val;
			}
			if (p2 != null) {
				val += p2.val;
			}
			val = plus + val;
			plus = val / 10;
			cur.next = new ListNode(val % 10);
			if (p1 != null) {
				p1 = p1.next;
			}
			if (p2 != null) {
				p2 = p2.next;
			}
			cur = cur.next;
		}
		return rs;

}
}

三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
     List<List<Integer>> ans = new ArrayList();
        int len = nums.length;
        if(nums == null || len < 3) {
            return ans;
            }
        Arrays.sort(nums); // 排序
        for (int i = 0; i < len ; i++) {
            if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
        if(i > 0 && nums[i] == nums[i-1]){
            continue;
            } // 去重
            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                    while (L<R && nums[L] == nums[L+1]){
                        L++;
                        } // 去重
                    while (L<R && nums[R] == nums[R-1]) {
                        R--;
                        } // 去重
                    L++;
                    R--;
                }
                else if (sum < 0){
                     L++;
                }
                else if (sum > 0) {
                     R--;
                }
            }
        }        
        return ans;
    }
}

四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> ans = new ArrayList<>();//保存答案
//        进行排序
        Arrays.sort(nums);
        if (nums.length<4){
            return ans;
        }
        for (int a = 0; a <nums.length-3 ; a++) {
//            去重
            if (a>0&&nums[a]==nums[a-1]){
               
                continue;
            }

            for (int b = a+1; b <nums.length-2 ; b++) {
                if (b>a+1&&nums[b]==nums[b-1]){
                
                continue;
            }
               int left=b+1;//左指针
               int right=nums.length-1;//右指针
                while (left<right){
                    if (nums[a]+nums[b]+nums[left]+nums[right]<target){
                        left++;
                    }else if (nums[a]+nums[b]+nums[left]+nums[right]>target){
                        right--;
                    }else {
//                        加入到答案中去
                        List<Integer> curans = new ArrayList<>();
                        curans.add(nums[a]);
                        curans.add(nums[b]);
                        curans.add(nums[left]);
                        curans.add(nums[right]);
                        ans.add(curans);
                        while (left<right&&nums[left]==nums[left+1]){
                            left++;
                        }
                        while (left<right&&nums[right]==nums[right-1]){
                            right--;
                        }
                        left++;
                        right--;
                    }

                }


            }
        }
        return ans;
    }
}


两数相除

class Solution {
    //利用递归,利用加法每次除到不能再相除
    public int divide(int dividend, int divisor) {
        if(dividend == 0)       return 0;
        if(dividend == Integer.MIN_VALUE && divisor == -1)  return Integer.MAX_VALUE;

        int sign = 1;
        if((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0))   
            sign = -1;
        
        //Integer.MAX Integer.MIN  盛
        dividend = dividend > 0 ? -dividend : dividend;
        divisor  = divisor > 0  ? -divisor  : divisor;

        return sign * helper(dividend, divisor);

    }
    /*
        坚定不移的相信, 这个函数可以帮你完成任务
    */
    public int helper(int a, int b){
        //a = -1,  b = -5
        if(a >= b)  return a > b ? 0 : 1;

        int count = 1;
        int res   = 0;
        int tb    = b;

        while(a <= tb && tb < 0){
            a -= tb;

            res += count;

            tb    += tb;
            count += count;
        }

        return res + helper(a, b);
    }
}

字符串

简化路径

给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 ‘/’ 开头),请你将其转化为更加简洁的规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,‘//’)都被视为单个斜杠 ‘/’ 。 对于此问题,任何其他格式的点(例如,‘…’)均被视为文件/目录名称。

请注意,返回的 规范路径 必须遵循下述格式:

始终以斜杠 ‘/’ 开头。
两个目录名之间必须只有一个斜杠 ‘/’ 。
最后一个目录名(如果存在)不能 以 ‘/’ 结尾。
此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 ‘.’ 或 ‘…’)。
返回简化后得到的 规范路径

输入:path = "/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。 
class Solution {
    public String simplifyPath(String path) {
      Stack<String> ansstack = new Stack<>();
        String[] split = path.split("/");
        StringBuilder ansstr = new StringBuilder();
        for (int i = 0; i <split.length ; i++) {
            if (!"..".contains(split[i])){
                ansstack.push(split[i]);
            }
            if (!ansstack.isEmpty()&&split[i].equals("..")){
                ansstack.pop();
            }
        }
        for (int i = 0; i <ansstack.size() ; i++) {
            ansstr.append("/"+ansstack.get(i));
        }
        if (ansstr.length()==0){
            return "/";
        }
        return ansstr.toString();
    }
}

字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

class Solution {
    //由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            char[] array = str.toCharArray();
            Arrays.sort(array);
            String key = new String(array);
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

无重复最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
利用滑动窗口,在窗口中维护不重复子串,如果出现重复,从左边探出直至,没有重复,
class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        HashSet<Character> tempset = new HashSet<>();
//定义右指针的长度
        int endindex=-1;
        int ans=0;
        for (int i = 0; i <s.length() ; i++) {
//            从i开始的
            if (i!=0){
                tempset.remove(s.charAt(i-1));
            }
            while (endindex+1<s.length() && !tempset.contains(s.charAt(endindex+1))){
                tempset.add(s.charAt(endindex+1));
                endindex++;
            }
            ans=Math.max(ans,endindex-i+1);
        }
            return ans;
    }
    }

验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写

class Solution {
    public boolean isPalindrome(String s) {
 char[] schars = s.toCharArray();
        StringBuilder strgood = new StringBuilder();
        //特殊字符的处理
        for (int i = 0; i <schars.length ; i++) {
            if (Character.isLetterOrDigit(schars[i])) {
                strgood.append(Character.toLowerCase(schars[i]));
            }
        }
        char[] check = strgood.toString().toCharArray();
        int left=0;
        int right=check.length-1;
        //看左右两边是否是一致的来判断是否回文
        while (left<right){
            if (check[left]!=check[right]){
                return false;
            }
            left++;
            right--;
        }
return true;
    }
}

最长回文子串

我们用一个 boolean dp[l][r] 表示字符串从 i 到 j 这段是否为回文。试想如果 dp[l][r]=true,我们要判断 dp[l-1][r+1] 是否为回文。只需要判断字符串在(l-1)和(r+1)两个位置是否为相同的字符,是不是减少了很多重复计算。
进入正题,动态规划关键是找到初始状态和状态转移方程。
初始状态,l=r 时,此时 dp[l][r]=true。
状态转移方程,dp[l][r]=true 并且(l-1)和(r+1)两个位置为相同的字符,此时 dp[l-1][r+1]=truepublic String longestPalindrome(String s) {
        if (s == null || s.length() < 2) {
            return s;
        }
        int strLen = s.length();
        int maxStart = 0;  //最长回文串的起点
        int maxEnd = 0;    //最长回文串的终点
        int maxLen = 1;  //最长回文串的长度

        boolean[][] dp = new boolean[strLen][strLen];

        for (int r = 1; r < strLen; r++) {
            for (int l = 0; l < r; l++) {
                if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
                    dp[l][r] = true;
                    if (r - l + 1 > maxLen) {
                        maxLen = r - l + 1;
                        maxStart = l;
                        maxEnd = r;

                    }
                }

            }

        }
        return s.substring(maxStart, maxEnd + 1);

    }

链表

环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

public class Solution {
    public boolean hasCycle(ListNode head) {
          HashSet<ListNode> check = new HashSet<>();
        ListNode index = head;
        while (index!=null){
            if (check.contains(index)){
                return true;
            }
            check.add(index);
            index=index.next;
        }
return false;
    }
}

环形链表2返回节点

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

public class Solution {
    public ListNode detectCycle(ListNode head) {
         HashSet<ListNode> check = new HashSet<>();
        ListNode index = head;
        while (index!=null){
            if (check.contains(index)){
                return index;
            }
            check.add(index);
            index=index.next;
        }
        return null;
    }
}

重排链表、、

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

class Solution {
    //链表保存节点,更改位置
    public void reorderList(ListNode head) {
 ArrayList<ListNode> help = new ArrayList<>();
        ListNode index = head;
        while (index!=null){
            help.add(index);
            index=index.next;
        }
        int left=0;
        int right=help.size()-1;
        while (left<right){
            ListNode startNode = help.get(left);
            ListNode endNode = help.get(right);
            endNode.next=startNode.next;
            startNode.next=endNode;
            left++;
            right--;
        }
        help.get(left).next=null;
    }
}

反转链表

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode leftinde=head;
        ListNode rightindex=head;
        int conut=1;
        while (conut<left){
            leftinde=leftinde.next;
            conut++;
        }
        conut=(right+1-left)/2;
        int index=right-left;
        while (conut>0){
          ListNode curindex=leftinde;
          int temp=0;
          while (temp!=index){
              temp++;
              curindex= curindex.next;
          }
          
          int tempval=curindex.val;
          curindex.val=leftinde.val;
          leftinde.val=tempval;
          leftinde=leftinde.next;
          index=index-2;
            conut--;
            
        }
    return head;
    
    }
}

分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

//保存小节点和大节点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
   public ListNode partition(ListNode head, int x) {
        ListNode small = new ListNode(0);
        ListNode smallHead = small;
        ListNode large = new ListNode(0);
        ListNode largeHead = large;
        while (head != null) {
            if (head.val < x) {
                small.next = head;
                small = small.next;
            } else {
                large.next = head;
                large = large.next;
            }
            head = head.next;
        }
        large.next = null;
        small.next = largeHead.next;
        return smallHead.next;
    }
}


删除排序链表中的重复元素 II

给定一个已排序的链表的头 head删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return head;
        }
        
        ListNode dummy = new ListNode(0, head);

        ListNode cur = dummy;
        while (cur.next != null && cur.next.next != null) {
            if (cur.next.val == cur.next.next.val) {
                int x = cur.next.val;
                while (cur.next != null && cur.next.val == x) {
                    cur.next = cur.next.next;
                }
            } else {
                cur = cur.next;
            }
        }

        return dummy.next;
    }
}

旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 ‘

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cCoj1F8-1657510180983)(D:\学习资料\img\1657198007333.png)]

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head==null){return null;}
        //        首先找到链表的个数
        int count = 0;//geshu
        ListNode index = head;
        while (index.next != null) {
            count = count + 1;
            index = index.next;
        }
        count = count + 1;
        System.out.println(count);
if(count==1){return head;}
        int location = k % count;
        System.out.println(location);
//变成闭环
         index = head;
        while (true){
            if (index.next==null){
                index.next=head;
                break;
            }
            index=index.next;
        }

         int reverse=(count+1-location)-1;
        int key=1;
        index=head;
        ListNode ans=null;
        while (key<=count){
            if (key==reverse){
                ans=index.next;
                index.next=null;
                return ans;
            }
            key++;
            index=index.next;
        }

        return ans;
    }
    }

删除链表倒数第N个节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
   int size=1;
      ListNode curentnode=head;
      while (curentnode.next!=null){
          curentnode = curentnode.next;
          size++;
      }
      int zhengshu=size+1-n;
      
      
        if (zhengshu==1){
            head=head.next;
            return head;
        }
       
        ListNode preNode=head;
      curentnode=head.next;
       int count=2;
       while (count<zhengshu){
            preNode = preNode.next;
            curentnode = curentnode.next;
           count++;
       }
       preNode.next=curentnode.next;
        return head;
    }
}

合并两个有序链表

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1==null){
            return l2;
        }
        if (l2==null){
            return l1;
        }
        
        if (l1.val<l2.val){
            l1.next=mergeTwoLists(l1.next,l2);
            return l1;
        }else {
            l2.next=mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

两两交换料表中的节点

class Solution {
    public ListNode swapPairs(ListNode head) {
   if (head==null||head.next==null) {
            return head;
        }//终止条件是head为空或者head的下一个为空
        ListNode next=head.next;
        head.next=swapPairs(next.next);//
         next.next=head;//
        return next;
    }
}

二叉树

填充每一个节点的下一个右侧节点指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ps9wzHJp-1657510180984)(D:\学习资料\img\1657289295908.png)]

class Solution {
   //层序遍历的方法将每一个节点填充有指针
    public Node connect(Node root) {
        if(root==null)return root;
        ArrayDeque<Node> curnodes=new ArrayDeque<>();
        curnodes.add(root);
        while (!curnodes.isEmpty()){
         int size=curnodes.size();
            ArrayList<Node> nodes = new ArrayList<>();
            for (int i = 0; i <size ; i++) {
                //遍历节点的子节点
                Node curnode = curnodes.remove();
                nodes.add(curnode);
                if (curnode.left!=null){
                    curnodes.add(curnode.left);
                }
                if (curnode.right!=null){
                    curnodes.add(curnode.right);
                }
            }
            //填充指针
            for (int i = 1; i <nodes.size() ; i++) {
                Node pre = nodes.get(i - 1);
                Node cur = nodes.get(i);
                pre.next=cur;
            }
        }
        return root;
    }
}

填充每一个节点的下一个右侧节点指针2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9wB04yi-1657510180985)(D:\学习资料\img\1657289489719.png)]

class Solution {
     public Node connect(Node root) {
        if(root==null)return root;
        ArrayDeque<Node> curnodes=new ArrayDeque<>();
        curnodes.add(root);
        while (!curnodes.isEmpty()){
         int size=curnodes.size();
            ArrayList<Node> nodes = new ArrayList<>();
            for (int i = 0; i <size ; i++) {
                Node curnode = curnodes.remove();
                nodes.add(curnode);
                if (curnode.left!=null){
                    curnodes.add(curnode.left);
                }
                if (curnode.right!=null){
                    curnodes.add(curnode.right);
                }
            }
            for (int i = 1; i <nodes.size() ; i++) {
                Node pre = nodes.get(i - 1);
                Node cur = nodes.get(i);
                pre.next=cur;
            }
        }
        return root;
    }
}

二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

class Solution {
    public void flatten(TreeNode root) {
        List<TreeNode> list = new ArrayList<TreeNode>();
        preorderTraversal(root, list);
        int size = list.size();
        for (int i = 1; i < size; i++) {
            TreeNode prev = list.get(i - 1), curr = list.get(i);
            prev.left = null;
            prev.right = curr;
        }
    }
//将二叉树进行前序遍历同时记录顺序
    public void preorderTraversal(TreeNode root, List<TreeNode> list) {
        if (root != null) {
            list.add(root);
            preorderTraversal(root.left, list);
            preorderTraversal(root.right, list);
        }
    }
}

二叉树层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点


class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
  ArrayList<List<Integer>> ans = new ArrayList<>();
        ArrayDeque<TreeNode> curNodes = new ArrayDeque<TreeNode>();
        if(root==null){
            return ans;
        }
        curNodes.add(root);
        while (!curNodes.isEmpty()){
            ArrayList<Integer> temp = new ArrayList<>();
            int cursize=curNodes.size();
//            此时需要遍历当前队列中的所有元素的节点,并且把当前队列的节点所有的子节点全部装入的队列中去
            for (int i = 0; i <cursize ; i++) {
                TreeNode curnode = curNodes.remove();
//                装入集合中去
                temp.add(curnode.val);
                if (curnode.left!=null){
                    curNodes.add(curnode.left);
                }
                if (curnode.right!=null){
                    curNodes.add(curnode.right);
                }
                
            }
            ans.add(temp);
        }
        return ans;
    }
}

二叉树的层序遍历自底向上


class Solution {
       public List<List<Integer>> levelOrderBottom(TreeNode root) {
        LinkedList<List<Integer>> ans = new LinkedList<>();
        if (root==null){
            return ans;
        }
        ArrayDeque<TreeNode> curnode = new ArrayDeque<>();
        curnode.add(root);
        while (!curnode.isEmpty()){
            int size=curnode.size();
            ArrayList<Integer> curans = new ArrayList<>();
            for (int i = 0; i <size ; i++) {
                TreeNode remove = curnode.remove();
                curans.add(remove.val);
                if (remove.left!=null){
                    curnode.add(remove.left);
                }
                if (remove.right!=null){
                    curnode.add(remove.right);
                }
            }
            ans.addFirst(curans);
        }
        return new ArrayList<List<Integer>>(ans);
    }
}

将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。


class Solution {
     public TreeNode sortedArrayToBST(int[] nums) {
       return dfs(nums,0,nums.length-1);
    }

    private TreeNode dfs(int[] nums, int start, int end) {
        if (start>end){
            return null;
        }
        int mid=(start+end)/2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left=dfs(nums,start,mid-1);
        root.right=dfs(nums,mid+1,end);
        return root;
    }

}

将有序链表转换为二叉搜索树


class Solution {
    public TreeNode sortedListToBST(ListNode head) {
    if (head==null){
            return null;
        }
        if (head.next == null) {
            return new TreeNode(head.val);
        }
        ListNode fast=head;
        ListNode slow=head;
        ListNode pre=null;
        while (fast!=null&&fast.next!=null){
             pre=slow;
            fast=fast.next.next;
            slow=slow.next;
        }
        TreeNode root = new TreeNode(slow.val);
        pre.next=null;
        root.left=sortedListToBST(head);
        root.right=sortedListToBST(slow.next);
        return root;
    }
  
}

判断是否平衡二叉树

class Solution {
    public boolean isBalanced(TreeNode root) {
		if(root == null) return true;
		Stack<TreeNode> stack = new Stack<>();
		TreeNode pre = null;
		while(root != null || !stack.isEmpty()) {
			if(root != null) {
				stack.push(root);
				root = root.left;
			}else {
				TreeNode inNode = stack.pop();
				int left = getHigh(inNode.left);
				int right = getHigh(inNode.right);
				if(Math.abs(left - right) > 1 ) return false;
				root = inNode.right;
			}
		}
		return true;
	 }
	 public int getHigh(TreeNode node) {
		 //层序遍历且左右子树高度
		 int res = 0;
		 if(node == null) return res;
		 Queue<TreeNode> que = new LinkedList<>();
		 que.offer(node);
		 while(!que.isEmpty()) {
			 int size = que.size();
			 res++;
			 while(size--> 0) {
				 TreeNode tem = que.poll();
				 if(tem.left != null) que.offer(tem.left);
				 if(tem.right != null) que.offer(tem.right);
				 
			 }
		 }
		 return res;
	 }
}

二叉树的锯齿遍历

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)


class Solution {
    //定义基数或者偶数为决定遍历的方向
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
      ArrayList<List<Integer>> ans = new ArrayList<>();
        ArrayDeque<TreeNode> curNodes = new ArrayDeque<TreeNode>();
        if(root==null){
            return ans;
        }
        curNodes.add(root);
        int count=0;
        while (!curNodes.isEmpty()){
            LinkedList<Integer> temp = new LinkedList<>();
            int cursize=curNodes.size();
//            此时需要遍历当前队列中的所有元素的节点,并且把当前队列的节点所有的子节点全部装入的队列中去
            for (int i = 0; i <cursize ; i++) {
                TreeNode curnode = curNodes.remove();
//                装入集合中去
                if (count%2==0){
                    temp.add(curnode.val);
                }else {
                    temp.addFirst(curnode.val);
                }
                if (curnode.left!=null){
                    curNodes.add(curnode.left);
                }
                if (curnode.right!=null){
                    curNodes.add(curnode.right);
                }

            }
            count++;
            ans.add(new ArrayList<Integer>(temp));
        }
        return ans;
    }
}

二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

class Solution {
    public int maxDepth(TreeNode root) {
      
      if(root==null){
        return 0;
      }
      int left=maxDepth(root.left);
      int right=maxDepth(root.right);
      return Math.max(left,right)+1;
    }

}

二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

class Solution {
   public int minDepth(TreeNode root) {
    if(root == null) {
        return 0;
    } else if(root.left == null && root.right == null) {
        return 1;
    } else if(root.left == null) {
        return minDepth(root.right) + 1;
    } else if(root.right == null) {
        return minDepth(root.left) + 1;
    }
    return Math.min(minDepth(root.left) + 1, minDepth(root.right) + 1);
}
}

根节点到叶子结点路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if (root == null) {
            return false;
        }
        if (root.left == null && root.right == null) {
            return sum == root.val;
        }
        return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
    }
}

根节点到叶子结点路径总和2

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。


class Solution {
    private List<List<Integer>> anslist;
    private List<Integer> curlist;
    private List<List<Integer>> ans;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        anslist=new ArrayList<>();
        ans=new ArrayList<>();
        curlist=new ArrayList<Integer>();
        dfs(root,curlist,targetSum);
        for (int i = 0; i <anslist.size() ; i++) {
            List<Integer> integers = anslist.get(i);
            int sum=0;
            for (int j = 0; j <integers.size() ; j++) {
                sum=sum+integers.get(j);
            }
            if (sum==targetSum){
                ans.add(integers);
            }
        }
        return ans;
    }
     private void dfs(TreeNode root, List<Integer> curlist, int targetSum) {
        if (root==null){
            return;
        }
        if (root.right==null&&root.left==null){
            curlist.add(root.val);
            anslist.add(new ArrayList<>(curlist));
            curlist.remove(curlist.size()-1);
            return;
        }
        curlist.add(root.val);
        dfs(root.left,curlist,targetSum);
        dfs(root.right,curlist,targetSum);
        curlist.remove(curlist.size()-1);
    }
}

求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。

从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0re2KIBH-1657510180988)(D:\学习资料\img\1657205228527.png)]

class Solution {
	public TreeNode buildTree(int[] preorder, int[] inorder) {
		if(preorder.length==0 || inorder.length==0) {
			return null;
		}
        //前序遍历到的第一个就是根节点,中序遍历的根节点元素左边就是左子树的中序遍历,右边就是右子树的中序遍历,
        
		//根据前序数组的第一个元素,就可以确定根节点
		TreeNode root = new TreeNode(preorder[0]);
		for(int i=0;i<preorder.length;++i) {
			//用preorder[0]去中序数组中查找对应的元素
			if(preorder[0]==inorder[i]) {
				//将前序数组分成左右两半,再将中序数组分成左右两半
				//之后递归的处理前序数组的左边部分和中序数组的左边部分
				//递归处理前序数组右边部分和中序数组右边部分
				int[] pre_left = Arrays.copyOfRange(preorder,1,i+1);
				int[] pre_right = Arrays.copyOfRange(preorder,i+1,preorder.length);
				int[] in_left = Arrays.copyOfRange(inorder,0,i);
				int[] in_right = Arrays.copyOfRange(inorder,i+1,inorder.length);
				root.left = buildTree(pre_left,in_left);
				root.right = buildTree(pre_right,in_right);
				break;
			}
		}
		return root;
	}
}

从后序与中序遍历序列构造二叉树


class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
       return buildTree(inorder, 0, inorder.length,
                        postorder, 0, postorder.length);
    }

    private TreeNode buildTree(int[] inorder, int inLeft, int inRight,
                               int[] postorder, int postLeft, int postRight){
        
        if(inRight - inLeft < 1) return null;
        if (inRight - inLeft == 1) {
            return new TreeNode(inorder[inLeft]);
        }
        TreeNode root = new TreeNode(postorder[postRight - 1]);
        int rootIndex = 0;
        for(int i = inLeft; i < inRight; i++){
            if(inorder[i] == root.val){
                rootIndex = i;
            }
        }
        
        int LENGTH = rootIndex - inLeft;
        root.left = buildTree(inorder, inLeft, rootIndex,
                              postorder, postLeft, postLeft + LENGTH);
        root.right = buildTree(inorder, rootIndex + 1, inRight,
                               postorder,postLeft + LENGTH, postRight - 1);
        return root;
    }
}

二叉树中序遍历

/**
 * 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 {
    private List<Integer> ans;
    public List<Integer> inorderTraversal(TreeNode root) {
 if (root==null){
            return new ArrayList<Integer>();
        }
        ans=new ArrayList<Integer>();
        dfs(root);
        return ans;
    }
      public void dfs(TreeNode root) {
        if (root==null){
            return;
        }
      
       dfs(root.left);
        ans.add(root.val);
       dfs(root.right);
    }
}

不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

class Solution {
    /*标签:动态规划
假设 n 个节点存在二叉排序树的个数是 G (n),令 f(i) 为以 i 为根的二叉搜索树的个数,则
G(n) = f(1) + f(2) + f(3) + f(4) + ... + f(n)G(n)=f(1)+f(2)+f(3)+f(4)+...+f(n)

当 i 为根节点时,其左子树节点个数为 i-1 个,右子树节点为 n-i,则
f(i) = G(i-1)*G(n-i)f(i)=G(i−1)∗G(n−i)

综合两个公式可以得到 卡特兰数 公式
G(n) = G(0)*G(n-1)+G(1)*(n-2)+...+G(n-1)*G(0)G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0)*/

    public int numTrees(int n) {
        int[] dp = new int[n+1];
        dp[0]=1;
        dp[1]=1;
        for (int i = 2; i <n+1 ; i++) {
            for (int j = 1; j < i+1; j++) {
                dp[i]=dp[i]+dp[j-1]*dp[i-j];
            }

        }
        return dp[n];
    }
}

不同的二叉搜索树

给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。

class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) {
            return new LinkedList<TreeNode>();
        }
        return generateTrees(1, n);
    }

    public List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> allTrees = new LinkedList<TreeNode>();
        if (start > end) {
            allTrees.add(null);
            return allTrees;
        }

        // 枚举可行根节点
        for (int i = start; i <= end; i++) {
            // 获得所有可行的左子树集合
            List<TreeNode> leftTrees = generateTrees(start, i - 1);

            // 获得所有可行的右子树集合
            List<TreeNode> rightTrees = generateTrees(i + 1, end);

            // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
            for (TreeNode left : leftTrees) {
                for (TreeNode right : rightTrees) {
                    TreeNode currTree = new TreeNode(i);
                    currTree.left = left;
                    currTree.right = right;
                    allTrees.add(currTree);
                }
            }
        }
        return allTrees;
    }
}

验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。


class Solution {
      private ArrayList<Integer> res;
    public boolean isValidBST(TreeNode root) {
        res=new ArrayList<>();
        midorder(root);
        for (int i = 0; i <res.size()-1 ; i++) {
            if (res.get(i)>=res.get(i+1)){
                return false;
            }
        }
        return true;
    }
    public void midorder(TreeNode root) {
        if (root.left!=null){
            midorder(root.left);
        }
        res.add(root.val);
        if (root.right!=null){
            midorder(root.right);
        }
    }
}

回复二叉搜索树

给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树


class Solution {
    //用两个变量x,y来记录需要交换的节点
    private TreeNode x = null;
    private TreeNode y = null;
    private TreeNode pre = null;
    public void recoverTree(TreeNode root) {
        dfs(root);
        //如果x和y都不为空,说明二叉搜索树出现错误的节点,将其交换
        if(x!=null && y!=null) {
            int tmp = x.val;
            x.val = y.val;
            y.val = tmp;
        }
    }
	
    //中序遍历二叉树,并比较上一个节点(pre)和当前节点的值,如果pre的值大于当前节点值,则记录下这两个节点
    private void dfs(TreeNode node) {
        if(node==null) {
            return;
        }
        dfs(node.left);
        if(pre==null) {
            pre = node;
        }
        else {
            if(pre.val>node.val) {
                y = node;
                if(x==null) {
                    x = pre;
                }
            }
            pre = node;
        }
        dfs(node.right);
    }
}

相同的树

给你两棵二叉树的根节点 pq ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。


class Solution {
   public boolean isSameTree(TreeNode p, TreeNode q) {
    return dfs1(p,q);
    }
   public boolean dfs1(TreeNode p,TreeNode q){
        if(p==null&&q==null){
            return true;
        }
        if (p==null||q==null){
            return false;
        }
        if (p.val!=q.val){
            return false;
        }
       return dfs1(p.left,q.left)&&dfs1(p.right,q.right);


    }
}

对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。


class Solution {
	public boolean isSymmetric(TreeNode root) {
		if(root==null) {
			return true;
		}
		//调用递归函数,比较左节点,右节点
		return dfs(root.left,root.right);
	}
	
	boolean dfs(TreeNode left, TreeNode right) {
		//递归的终止条件是两个节点都为空
		//或者两个节点中有一个为空
		//或者两个节点的值不相等
		if(left==null && right==null) {
			return true;
		}
		if(left==null || right==null) {
			return false;
		}
		if(left.val!=right.val) {
			return false;
		}
		//再递归的比较 左节点的左孩子 和 右节点的右孩子
		//以及比较  左节点的右孩子 和 右节点的左孩子
		return dfs(left.left,right.right) && dfs(left.right,right.left);
	}
}

数组

最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

class Solution {
    public int longestConsecutive(int[] nums) {
 //       利用hash表的性质不可重复
        int res=0;
        Set<Integer> numsset = new HashSet<>();
        for (int i = 0; i <nums.length ; i++) {
            numsset.add(nums[i]);
        }
        for (int x:numsset){
            if (!numsset.contains(x-1)){//不包含x-1
                int y=x;//找到大于x连续的最大值
                while (numsset.contains(y+1)){
                    y++;
                }
                res=Math.max(res,y-x+1);//连续的数量
            }
        }
return res;
    }
}

搜索旋转排序数组 II

已知存在一个按非降序排列的整数数组 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,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。

public boolean search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start <= end) {
            mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[start] == nums[mid]) {
                start++;
                continue;
            }
            //前半部分有序
            if (nums[start] < nums[mid]) {
                //target在前半部分
                if (nums[mid] > target && nums[start] <= target) {
                    end = mid - 1;
                } else {  //否则,去后半部分找
                    start = mid + 1;
                }
            } else {
                //后半部分有序
                //target在后半部分
                if (nums[mid] < target && nums[end] >= target) {
                    start = mid + 1;
                } else {  //否则,去后半部分找
                    end = mid - 1;

                }
            }
        }
        //一直没找到,返回false
        return false;

    }

删除有序数组的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

class Solution {
     public int removeDuplicates(int[] nums) {
        if (nums.length<3){
            return nums.length;
        }
        int head=2;
        int end=2;
        while (end<nums.length){
            if (nums[end]!=nums[head-2]){
                nums[head]=nums[end];
                head++;
            }
            end++;
    }
        return head;
}
}

颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库的sort函数的情况下解决这个问题。

class Solution {
   public static void sortColors(int[] nums) {
        int count1=0;
        int count2=0;
        int count3=0;
        for (int i = 0; i <nums.length ; i++) {
            if (nums[i]==0){
                count1=count1+1;
            }
            if (nums[i]==1){
                count2=count2+1;
            }
            if (nums[i]==2){
                count3=count3+1;
            }
        }
        for (int i = 0; i <count1 ; i++) {
            nums[i]=0;
        }
        for (int i = count1; i <count1+count2 ; i++) {
            nums[i]=1;
        }
        for (int i = count1+count2; i <count1+count2+count3 ; i++) {
            nums[i]=2;
        }
    }
}

搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

class Solution {
 public boolean searchMatrix(int[][] matrix, int target) 
     //找到所在的行,然后进行二分查找
        int indexrow=0;
        int lie=matrix[0].length;
        for (int i = 0; i <matrix.length ; i++) {
            if (target>=matrix[i][0]&&target<=matrix[i][lie-1]){
                indexrow=i;
                break;
            }
        }
//        利用二分查找
        int left=0;
        int righ=lie-1;
        while (righ>=left){
            int midleval=matrix[indexrow][(left+righ)/2];
            if (midleval==target){
                return true;
            }
            if (target<midleval){
                righ=(left+righ)/2-1;
            }
            if (target>midleval){
                left=(left+righ)/2+1;
            }
        }
        return false;
    }
}

矩阵置零

给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。**

class Solution {
    //记录那一个位置为零,容易进行修改
  public void setZeroes(int[][] matrix) {
        ArrayList<int[]> adress = new ArrayList<>();
        for (int i = 0; i <matrix.length ; i++) {
            for (int j = 0; j <matrix[0].length ; j++) {
                if (matrix[i][j]==0){
                    int[] curindex = new int[2];
                    curindex[0]=i;
                    curindex[1]=j;
                    adress.add(curindex);
                }
                
            }
        }
        for (int i = 0; i <adress.size() ; i++) {
            int[] index = adress.get(i);
            for (int j = 0; j <matrix[0].length ; j++) {
                matrix[index[0]][j]=0;
            }
            for (int j = 0; j <matrix.length ; j++) {
                matrix[j][index[1]]=0;
            }
        }
    }
}

插入区间

给你一个 无重叠的 *,*按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]
class Solution {
   public int[][] insert(int[][]intervals,int[] newInternal){
        int[][] ansmartrix = new int[intervals.length + 1][2];
        for (int i = 0; i <intervals.length ; i++) {
            for (int j = 0; j <intervals[0].length ; j++) {
                ansmartrix[i][j]=intervals[i][j];
            }
        }
        ansmartrix[intervals.length][0]=newInternal[0];
        ansmartrix[intervals.length][1]=newInternal[1];
        //        首先对数组根据第一个数字进行排序

        for (int i = 0; i <ansmartrix.length ; i++) {
            for (int j = 0; j <ansmartrix.length-i-1 ; j++) {
                if (ansmartrix[j][0]>ansmartrix[j+1][0]){
//                    交换位置两行进行交换
                    int[] temp=ansmartrix[j];
                    ansmartrix[j]=ansmartrix[j+1];
                    ansmartrix[j+1]=temp;
                }
            }
        }
       
        List<List<Integer>> ans = new ArrayList<>();
        int[] visit=new int[ansmartrix.length];
        for (int i = 0; i <ansmartrix.length ; i++) {
            if (visit[i]==1){
                continue;
            }
            int[] temp=ansmartrix[i];
            visit[i]=1;
            for (int j = i+1; j <ansmartrix.length ; j++) {
                if (temp[1]>=ansmartrix[j][0]){
                    visit[j]=1;
                    temp[1]=Math.max(temp[1],ansmartrix[j][1]) ;
                }
            }
            List<Integer> tempans = new ArrayList<Integer>();
            tempans.add(temp[0]);
            tempans.add(temp[1]);
            ans.add(tempans);
        }
        int[][] ints = new int[ans.size()][2];
        for (int i = 0; i <ans.size() ; i++) {
            List<Integer> integers = ans.get(i);
            ints[i][0]=integers.get(0);
            ints[i][1]=integers.get(1);
        }
        return ints;
    }
}

合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

class Solution {
    public int[][] merge(int[][] martrix){
//        首先对数组根据第一个数字进行排序,然后比较每一个数组单元的结尾和下一个数组单元的开头值的大小关系

        for (int i = 0; i <martrix.length ; i++) {
            for (int j = 0; j <martrix.length-i-1 ; j++) {
                if (martrix[j][0]>martrix[j+1][0]){
//                    交换位置两行进行交换
                    int[] temp=martrix[j];
                    martrix[j]=martrix[j+1];
                    martrix[j+1]=temp;
                }
            }
        }
//遍历
        List<List<Integer>> ans = new ArrayList<>();
        int[] visit=new int[martrix.length];
        for (int i = 0; i <martrix.length ; i++) {
            if (visit[i]==1){
                continue;
            }
            int[] temp=martrix[i];
            visit[i]=1;
            for (int j = i+1; j <martrix.length ; j++) {
               if (temp[1]>=martrix[j][0]){
                   visit[j]=1;
                   temp[1]=Math.max(temp[1],martrix[j][1]) ;
               }
            }
            List<Integer> tempans = new ArrayList<Integer>();
            tempans.add(temp[0]);
            tempans.add(temp[1]);
            ans.add(tempans);
        }
        int[][] ints = new int[ans.size()][2];
        for (int i = 0; i <ans.size() ; i++) {
            List<Integer> integers = ans.get(i);
            ints[i][0]=integers.get(0);
            ints[i][1]=integers.get(1);
        }
        return ints;
    }
}

螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素

class Solution {
       public static List<Integer> spiralOrder(int[][] matrix){
//        思路:定义上下左右边界,每次按照右下左上的顺序进行遍历知道遍历到边界,每次遍历结束修改边界条件
            List<Integer> list = new ArrayList<Integer>();
            if(matrix == null || matrix.length == 0)
                return list;
            int m = matrix.length;
            int n = matrix[0].length;
            int i = 0;

            //统计矩阵从外向内的层数,如果矩阵非空,那么它的层数至少为1层
            int count = (Math.min(m, n)+1)/2;
            //从外部向内部遍历,逐层打印数据
            while(i < count) {
                for (int j = i; j < n-i; j++) {
                    list.add(matrix[i][j]);
                }
                for (int j = i+1; j < m-i; j++) {
                    list.add(matrix[j][(n-1)-i]);
                }

                for (int j = (n-1)-(i+1); j >= i && (m-1-i != i); j--) {
                    list.add(matrix[(m-1)-i][j]);
                }
                for (int j = (m-1)-(i+1); j >= i+1 && (n-1-i) != i; j--) {
                    list.add(matrix[j][i]);
                }
                i++;
            }
            return list;
        }

    }

旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

class Solution {
    public void rotate(int[][] matrix) {
        int Length = matrix.length;
//        首先交换行,其次在交换对角线
        for (int i = 0; i <Length/2 ; i++) {//进行航交换的首行
            int EndRow=Length-1-i;
            for (int j = 0; j <Length; j++) {//矩阵对称
                int temp=matrix[i][j];
                matrix[i][j]=matrix[EndRow][j];
                matrix[EndRow][j]=temp;
            }
        }

//        变化对角线
        for (int i = 0; i <Length ; i++) {
            for (int j = 0; j <i ; j++) {
                int temp=matrix[i][j];
                matrix[i][j]=matrix[j][i];
                matrix[j][i]=temp;
            }
        }
 
    }
}

删除数组重复项

给你一个 升序排列 的数组 nums ,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致

class Solution {
    public int removeDuplicates(int[] nums) {
int head=0;
        int end=1;
        while (end<nums.length){
            if (nums[head]==nums[end]){
                end++;
            }else {
                nums[head+1]=nums[end];
                head++;
                end++;
                
            }
        
        }
        return head+1;
    }
}

在排序数组中查找元素的最后和第一个位置

你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置

class Solution {
    private List<Integer> ans;
     public  int[] searchRange(int[] nums,int target){
        ans = new ArrayList<Integer>();
        binarysearch(nums,0,nums.length-1,target);
        System.out.println(ans);
//        找到最大的和最小的
        int[] ansarray=new int[2];
        int max = ans.get(0);
        int min=ans.get(0);
        for (int i = 0; i <ans.size() ; i++) {
            if (ans.get(i)>max){
                max=ans.get(i);
            }
            if (ans.get(i)<min){
                min=ans.get(i);
            }
        }
        ansarray[0]=min;
        ansarray[1]=max;
        return ansarray;
    }
    public void binarysearch(int[] nums,int left,int right,int target){
        if (left>right){
            ans.add(-1);
            ans.add(-1);
            return;
        }
        int midle=(left+right)/2;
        int midleval=nums[midle];
        if (target<midleval){
            binarysearch(nums,left,midle-1,target);
        }else if (target>midleval){
            binarysearch(nums,midle+1,right,target);
        }else {
            ans.add(midle);
            int temp=midle-1;
            while (true){
                if (temp<0||nums[temp]!=target){
                    break;
                }else {
                    ans.add(temp);
                    temp=temp-1;
                }
            }
             temp=midle+1;
            while (true){
                if (temp>nums.length-1||nums[temp]!=target){
                    break;
                }else {
                    ans.add(temp);
                    temp=temp+1;
                }
            }
        }
    }

}

外观数列

给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:

countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。


class Solution {
   public static String countAndSay(int n){
        if(n==1){
            return "1";
        }
        String string=countAndSay(n-1);
        char[] charn=string.toCharArray();
        List<Character> counts= new ArrayList<>();//保存出现次数
        List<Character> chars=new ArrayList<>();//保存字符
        int left=0;
        int right=0;
        int count=0;
        while (right<charn.length){
            if (charn[left]!=charn[right]){
                counts.add(Integer.toString(count).charAt(0));
                chars.add(charn[left]);
                left=right;
                count=0;
            }else {
                count=count+1;
                right=right+1;
            }
        }
        counts.add(Integer.toString(count).charAt(0));
        chars.add(charn[charn.length-1]);
        StringBuilder ans= new StringBuilder();
        for (int i = 0; i <counts.size() ; i++) {
            ans.append(counts.get(i));
            ans.append(chars.get(i));
        }
        return ans.toString();
    }
}

路径系列

三角形最小路径和

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

class Solution {
    //递归,dp[][]表示从顶点到当前位置的最小路径,最后返回最后一行的路径最小值
      public int minimumTotal(List<List<Integer>> triangle) {
       int[][] grid = new int[triangle.size()][triangle.size()];
        for (int i = 0; i <triangle.size() ; i++) {
            List<Integer> integers = triangle.get(i);
            for (int j = 0; j <integers.size() ; j++) {
                grid[i][j]=integers.get(j);
            }
        }
        return dp(grid);
    }
    private int dp(int[][] grid){
        int[][] dp = new int[grid.length][grid[0].length];
        dp[0][0]=grid[0][0];
        for (int i = 1; i <grid.length ; i++) {
            for (int j = 0; j <=i ; j++) {
                if (j==0){
                    dp[i][j]=grid[i][j]+dp[i-1][j];
                    continue;
                }
                if (i==j){
                    dp[i][j]=grid[i][j]+dp[i-1][j-1];
                    continue;
                }
                dp[i][j]=0;
            }
        }
        for (int i = 2; i <grid.length ; i++) {
            for (int j = 1; j <i ; j++) {
                dp[i][j]=grid[i][j]+Math.min(dp[i-1][j],dp[i-1][j-1]);
            }
        }
        int sum=Integer.MAX_VALUE;
        for (int i = 0; i <grid.length ; i++) {
            if (dp[grid.length-1][i]<sum){
                sum=dp[grid.length-1][i];
            }
        }
        return sum;
}
}
   

单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

 class Solution {

    private int[][] direction={{-1,0},{0,1},{1,0},{0,-1}};
    private boolean[][] visit;
    private char[] chars;
    public static void main(String[] args) {

    }

    /**
     * 采用回溯的算法像四个方向判断是否为一样的
     * @param board
     * @param word
     * @return
     */
    public boolean exist(char[][] board, String word) {
        visit=new boolean[board.length][board[0].length];
        int row=board.length;
        int lie =board[0].length;
        chars = word.toCharArray();
        for (int i = 0; i <row ; i++) {
            for (int j = 0; j <lie ; j++) {
                if (dfs(i,j,0,chars,board)){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 
     * @param x 当前的x坐标
     * @param y 当前的y坐标
     * @param index 当前的字符串的下标位置
     * @return
     */
    public boolean dfs(int x,int y,int index,char[] str,char[][] board){
        if (index==str.length-1&&str[index]==board[x][y]&&!visit[x][y]){
            return true;
        }
        if (str[index]==board[x][y]&&!visit[x][y]){//当前的位置相同
            visit[x][y]=true;
            //向四个方向寻找
            for (int i = 0; i <direction.length ; i++) {
               int newx=x+ direction[i][0];
               int newy=y+ direction[i][1];
                if (newx>=0&&newx<=board.length-1&&newy>=0&&newy<=board[0].length-1&&!visit[newx][newy]) {
                   if (dfs(newx, newy, index + 1, str, board)) {
                       return true;
                   }
               }
               continue;
            }
        }
        visit[x][y]=false;
        return false;
    }
}

不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

class Solution {
    public int uniquePaths(int m, int n) {
        return dfs(new HashMap<Pair,Integer>(), 0, 0, m, n);
    }

    private int dfs(Map<Pair,Integer> cache, int i, int j, int m, int n) {
        Pair p = new Pair(i,j);
        //如果(i,j)在缓存中则直接返回
        if(cache.containsKey(p)) {
            return cache.get(p);
        }
        //到达边界时,返回 1
        if(i == m - 1 || j == n - 1) {
            return 1;
        }
        //继续递归调用,往下i+1,往右j+1
        cache.put(p, dfs(cache, i + 1, j, m, n) + dfs(cache, i, j + 1, m, n) );
        return cache.get(p);
    }
}
//利用动态规划的思想,到达一个点的路径长度为左边上边路径的总数
class Solution {
    public int uniquePaths(int m, int n) {
  int[][] dp = new int[m][n];
        for (int k = 0; k <m ; k++) {
            dp[k][0]=1;
        }
        for (int k = 0; k <n ; k++) {
            dp[0][k]=1;
        }
        for (int k = 1; k <m ; k++) {
            for (int l = 1; l <n ; l++) {
                dp[k][l]=dp[k-1][l]+dp[k][l-1];
            }

        }
        return dp[m-1][n-1];
    }
}

不同路径2

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid){
         int row = obstacleGrid.length;
        int lie=obstacleGrid[0].length;
        if (row==1){
            for (int i = 0; i <obstacleGrid[0].length ; i++) {
                if (obstacleGrid[0][i]==1){
                    return 0;
                }
            }
        }
        if (lie==1){
            for (int i = 0; i <obstacleGrid.length ; i++) {
                if (obstacleGrid[i][0]==1){
                    return 0;
                }
            }
        }
        int[][] dp = new int[row][lie];
//        初始化第一列,考虑存在障碍物的情况
        for (int i = 0; i <obstacleGrid.length ; i++) {
            if (obstacleGrid[i][0]==1){
                for (int j = i; j <obstacleGrid.length ; j++) {
                    dp[j][0]=0;
                }
                break;
            }
            dp[i][0]=1;
        }
//初始化第一行,考虑存在障碍物的情况
        for (int i = 0; i <obstacleGrid[0].length ; i++) {
            if (obstacleGrid[0][i]==1){
                for (int j = i; j <obstacleGrid[0].length ; j++) {
                    dp[0][i]=0;
                }
                break;
            }
            dp[0][i]=1;
        }
        for (int i = 1; i <obstacleGrid.length ; i++) {
            for (int j = 1; j <obstacleGrid[0].length ; j++) {
                if (obstacleGrid[i][j]==1){
                    dp[i][j]=0;
                    continue;
                }
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
return dp[dp.length-1][dp[0].length-1];
    }
}

最小路径和

给定一个包含非负整数的 *m* x *n* 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

class Solution {
    //每一个点的最小距离为到左边点的最小距离和最上边最小距离最小值加上当期的值
    public int minPathSum(int[][] grid) {
 int row = grid.length;
        int lie = grid[0].length;
        if (row==1){
            int sum=0;
            for (int i=0;i<grid[0].length;i++){
                sum=sum+grid[0][i];
            }
            return sum;
        }
        if (lie==1){
            int sum=0;
            for (int i=0;i<grid.length;i++){
                sum=sum+grid[i][0];
            }
            return sum;
        }
        int[][] dp=new int[row][lie];
        int sum=0;
        for (int i=0;i<grid[0].length;i++){
            sum=sum+grid[0][i];
            dp[0][i]=sum;
        }
         sum=0;
        for (int i=0;i<grid.length;i++){
            sum=sum+grid[i][0];
            dp[i][0]=sum;
        }
        for (int i = 1; i <grid.length ; i++) {
            for (int j = 1; j <grid[0].length ; j++) {
                dp[i][j]=grid[i][j]+Math.min(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[row-1][lie-1];
        
    }
}

排列组合

数组元素组合3

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
class Solution {
    private List<List<Integer>> ans;
    private List<Integer> curans;
    private boolean[] check;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        check=new boolean[nums.length];
        curans=new ArrayList<Integer>();
        ans=new ArrayList<List<Integer>>();
          for (int i = 0; i <nums.length ; i++) {
            for (int j = 0; j <nums.length-i-1 ; j++) {
                if (nums[j]<nums[j+1]){
                    int temp=nums[j];
                    nums[j]=nums[j+1];
                    nums[j+1]=temp;
                }
            }
        }


        for (int i = 0; i <=nums.length ; i++) {
            dfs(0,nums,i,curans,check);
        }
        return ans;
    }

    public void dfs(int curindex,int[] nums,int n,List<Integer> curans,boolean[] check){
        if (curans.size()==n){
            List<Integer> integers = new ArrayList<>(curans);
            ans.add(integers);
            return;
        }
        for (int i = curindex; i <nums.length ; i++) {
            if (check[i]==true){
                continue;
            }
            if (i!=0&&nums[i-1]==nums[i]&&check[i-1]==false){
                continue;
            }
            curans.add(nums[i]);
            check[i]=true;
            dfs(i+1,nums,n,curans,check);
            curans.remove(curans.size()-1);
            check[i]=false;
        }
    }
}

数组元素组合2

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
class Solution {
    private List<List<Integer>> ans;
    private List<Integer> curans;
    public List<List<Integer>> subsets(int[] nums) {
        ans=new ArrayList<List<Integer>>();
        curans=new ArrayList<Integer>();
        for (int i = 0; i <nums.length+1 ; i++) {
            dfs(nums,0,0,i,curans);
        }

        return ans;
    }
    /**
     * @param nums 数组
     * @param index 当前的下标
     * @param cruk 当前的个数
     * @param k 需要的个数
     * @param curans 保存答案的list
     */
        public void dfs(int[] nums,int index,int cruk,int k,List<Integer> curans){
        if (cruk==k){
            List<Integer> curans1=new ArrayList<Integer>(curans);
            ans.add(curans1);
            return;
        }
        for (int i =index; i <nums.length ; i++) {
            curans.add(nums[i]);
            dfs(nums,i+1,cruk+1,k,curans);
            curans.remove(curans.size()-1);
        }
    }
}

数组元素组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

class Solution {
    private List<List<Integer>> ans;
    private List<Integer> curans;
     public List<List<Integer>> combine(int n, int k) {
        ans=new ArrayList<List<Integer>>();
        curans=new ArrayList<Integer>();
        int[] nums=new int[n];
        for (int i = 0; i <n ; i++) {
            nums[i]=i+1;
        }
        dfs(nums,0,0,k,curans);
        return ans;
    }
    /**
     * @param nums 数组
     * @param index 当前的下标
     * @param cruk 当前的个数
     * @param k 需要的个数
     * @param curans 保存答案的list
     */
    public void dfs(int[] nums,int index,int cruk,int k,List<Integer> curans){
        if (cruk==k){
            List<Integer> curans1=new ArrayList<Integer>(curans);
            ans.add(curans1);
            return;
        }
        for (int i =index; i <nums.length ; i++) {
            curans.add(nums[i]);
            dfs(nums,i+1,cruk+1,k,curans);
            curans.remove(curans.size()-1);
        }
    }
}

全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
    private  List<List<Integer>> ans;
    private List<Integer> curans;
    private int length;
     public List<List<Integer>> permute(int[] nums){
        ans=new ArrayList<List<Integer>>();
        curans=new ArrayList<Integer>();
        length=nums.length;
        dfs(nums, 0);
        System.out.println(ans);
        return ans;
    }

    private void dfs(int[] nums, int index) {
        if (curans.size()==length){
            ArrayList<Integer> integers = new ArrayList<>(curans);
            ans.add(integers);
            return;
        }
        for (int i = index; i <length ; i++) {
            boolean falg=true;
            int num = nums[i];
            for (int j = 0; j <curans.size() ; j++) {
                if (num==curans.get(j)){
                    falg=false;
                }
            }
            if (falg==false){
                continue;
            }
            curans.add(nums[i]);
            dfs(nums,0);
            curans.remove(curans.size()-1);
        }
    }
}

全排列2

定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]
class Solution {
    private List<List<Integer>> ans;
    private List<Integer> curans;
    private int length;
    private int[] visit;
     public List<List<Integer>>  permuteUnique(int[] nums){
        ans=new ArrayList<List<Integer>>();
        curans=new ArrayList<Integer>();
        length=nums.length;
        visit=new int[length];
        Arrays.sort(nums);
        for (int i = 0; i <visit.length ; i++) {
            visit[i]=0;
        }
        dfs(nums,0);
        return ans;
    }
    public void dfs(int[] nums,int index){
        if (curans.size()==length){ 
            ans.add(new ArrayList<Integer>(curans));
            return;
        }

        for (int i = index; i <length ; i++) {
          if (visit[i]==1){
                continue;
            }
            if (i>index&&nums[i]==nums[i-1]&&visit[i-1]==0){
                continue;
            }
            visit[i]=1;
            curans.add(nums[i]);
            dfs(nums,index);
            visit[i]=0;
            curans.remove(curans.size()-1);
        }
    }
}

总数组合

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
class Solution {
    private List<List<Integer>> res;//用来保存最后结果
    private int length;
    private List<Integer> curans;//保存当前的结果
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        res=new ArrayList<List<Integer>>();
        curans=new ArrayList<Integer>();
        length=candidates.length;
        if (length==0){
            return res;
        }
        dfs(candidates,0,target);
        return res;
    }
    private void dfs(int[] candidates, int index,int target) {
        if (target<0){
            return;
        }
        if (target==0){
            ArrayList<Integer> integers = new ArrayList<>(curans);
            res.add(integers);
            return;
        }
        for (int i = index; i <length ; i++) {
            curans.add(candidates[i]);
            dfs(candidates,i,target-candidates[i]);
            curans.remove(curans.size()-1);
        }

    }
}


总数组合2

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

   private int length;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        if (candidates.length==0){
            return ans;
        }
        Arrays.sort(candidates);
        ans =new ArrayList<List<Integer>>();
        curans=new ArrayList<Integer>();
        length=candidates.length;
        dfs(candidates,0,target);
        return ans;
    }
    private void dfs(int[] candidates,int index, int target) {
        if (target==0){
            ArrayList<Integer> integers = new ArrayList<>(curans);
            ans.add(integers);
            return;
        }
        if (target<0){
            return;
        }
        for (int i =index; i <length ; i++) {
            if (i>index&&candidates[i]==candidates[i-1]){
                continue;
            }
            curans.add(candidates[i]);
            dfs(candidates,i+1,target-candidates[i]);
            curans.remove(curans.size()-1);
        }
}
}

跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置

nums = [2,3,1,1,4]
输出: 2
class Solution {
    //在没一次跳的最大的范围中选择能跳的最远距离
    public int jump(int[] nums) {
         if (nums.length==0||nums.length==1){
            return 0;
        }
        int end = nums[0] + 1;
        int start = 0;
        int maxpro = 0;
        int count = 1;
//设置边界
        while (end < nums.length) {
//        在没一次跳的最大的范围中选择能跳的最远距离
            for (int i = 0; i < end; i++) {
                if (maxpro < i + nums[i]) {
                    maxpro = i + nums[i];
                }
            }
            count++;
//        找到最大的能跳的最远距离之后
            start = end;
            end = maxpro + 1;


        }
        return count;
    }
}

跳跃游戏2

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

class Solution {
    public boolean canJump(int[] nums) {
      if(nums.length==1) return true;
        int length=nums.length-1;
        int maxindex=0;
        int tmp=0;
        for (int i = 0; i < nums.length; i++) {
            tmp=i+nums[i];
            if(tmp==maxindex && nums[i]==0) {
                
                return false;
            }
            maxindex=Math.max(tmp, maxindex);
            if(maxindex>=length) {//跳跃到最后的位置
                
                return true;
            }
        }
        return false;
    }

}

电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
    private String letterMap[] = {
            " ",    //0
            "",     //1
            "abc",  //2
            "def",  //3
            "ghi",  //4
            "jkl",  //5
            "mno",  //6
            "pqrs", //7
            "tuv",  //8
            "wxyz"  //9
    };

    private ArrayList<String> res;

    public List<String> letterCombinations(String digits) {
        res = new ArrayList<String>();
        if(digits.equals(""))
            return res;

        findCombination(digits, 0, "");
        return res;
    }

    private void findCombination(String digits, int index, String s){
        if(index == digits.length()){
            res.add(s);
            return;
        }

        Character c = digits.charAt(index);
        String letters = letterMap[c - '0'];
        for(int i = 0 ; i < letters.length() ; i ++){
            findCombination(digits, index+1, s + letters.charAt(i));
        }

        return;
    }

}

下一个排列

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。

class Solution {
    public void nextPermutation(int[] nums) {
       if (nums.length<2){
           return;
       }
//       找到相邻升序的位置且在最右边
        int left=nums.length-2;
       int right=nums.length-1;
       while (left>=0){
           if (nums[left]<nums[right]){
               break;
           }
           left--;
           right--;
       }
//       判断是否找到,如果没有则直接返回升序排列
        if (left<0){
            Arrays.sort(nums);
            return;
        }else {
//            找到了,寻找right右边比left大的值,且最靠右,所以需要从最右边开始寻找
            
            if (right==nums.length-1){
//                直接交换返回
                int temp=nums[left];
                nums[left]=nums[right];
                nums[right]=temp;
                return;
            }else {
                int k=nums.length-1;
                while (k>right){
                    if (nums[k]>nums[left]){
                        break;
                    }
                    k--;
                }
//            把k和left的位置交换
                int temp=nums[left];
                nums[left]=nums[k];
                nums[k]=temp;
//            right后边按照升序进行排列
                for (int i = right; i <nums.length ; i++) {
                    for (int j = right; j <nums.length-1; j++) {
                        if (nums[j]>nums[j+1]){
                            temp=nums[j];
                            nums[j]=nums[j+1];
                            nums[j+1]=temp;
                        }
                    }
                }
                
            }
            }
        }
    }

格雷编码

class Solution {
        List<Integer> res = new ArrayList<>();
      public List<Integer> grayCode(int n) {
       dfs(n,new StringBuffer(),new int[]{0,1});
        return res;
    }

    /**
     *
     * @param n 层数
     * @param stringBuilder
     *
     */
    public void dfs(int n, StringBuffer sb, int[] nums){
 
        if(sb.length() == n){
        
            res.add(Integer.valueOf(sb.toString(),2));
            return;
        }
        sb.append(nums[0]);
        dfs(n,sb,new int[]{0,1});
        sb.deleteCharAt(sb.length()-1);
        sb.append(nums[1]);
        dfs(n,sb,new int[]{1,0});
        sb.deleteCharAt(sb.length()-1);
    }
}

死锁

Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
    synchronized (A) {
        log.debug("lock A");
        sleep(1);
        synchronized (B) {
            log.debug("lock B");
            log.debug("操作...");
        }
    }
}, "t1");
Thread t2 = new Thread(() -> {
    synchronized (B) {
        log.debug("lock B");
        sleep(0.5);
        synchronized (A) {
            log.debug("lock A");
            log.debug("操作...");
        }
    }
}, "t2");
t1.start();
t2.start();

活锁

public class TestLiveLock {
    static volatile int count = 10;
    static final Object lock = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            // 期望减到 0 退出循环
            while (count > 0) {
                sleep(0.2);
                count--;
                log.debug("count: {}", count);
            }
        }, "t1").start();
        new Thread(() -> {
            // 期望超过 20 退出循环
            while (count < 20) {
                sleep(0.2);
                count++;
                log.debug("count: {}", count);
            }
        }, "t2").start();
    }
}

下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
    private String letterMap[] = {
            " ",    //0
            "",     //1
            "abc",  //2
            "def",  //3
            "ghi",  //4
            "jkl",  //5
            "mno",  //6
            "pqrs", //7
            "tuv",  //8
            "wxyz"  //9
    };

    private ArrayList<String> res;

    public List<String> letterCombinations(String digits) {
        res = new ArrayList<String>();
        if(digits.equals(""))
            return res;

        findCombination(digits, 0, "");
        return res;
    }

    private void findCombination(String digits, int index, String s){
        if(index == digits.length()){
            res.add(s);
            return;
        }

        Character c = digits.charAt(index);
        String letters = letterMap[c - '0'];
        for(int i = 0 ; i < letters.length() ; i ++){
            findCombination(digits, index+1, s + letters.charAt(i));
        }

        return;
    }

}

下一个排列

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。

class Solution {
    public void nextPermutation(int[] nums) {
       if (nums.length<2){
           return;
       }
//       找到相邻升序的位置且在最右边
        int left=nums.length-2;
       int right=nums.length-1;
       while (left>=0){
           if (nums[left]<nums[right]){
               break;
           }
           left--;
           right--;
       }
//       判断是否找到,如果没有则直接返回升序排列
        if (left<0){
            Arrays.sort(nums);
            return;
        }else {
//            找到了,寻找right右边比left大的值,且最靠右,所以需要从最右边开始寻找
            
            if (right==nums.length-1){
//                直接交换返回
                int temp=nums[left];
                nums[left]=nums[right];
                nums[right]=temp;
                return;
            }else {
                int k=nums.length-1;
                while (k>right){
                    if (nums[k]>nums[left]){
                        break;
                    }
                    k--;
                }
//            把k和left的位置交换
                int temp=nums[left];
                nums[left]=nums[k];
                nums[k]=temp;
//            right后边按照升序进行排列
                for (int i = right; i <nums.length ; i++) {
                    for (int j = right; j <nums.length-1; j++) {
                        if (nums[j]>nums[j+1]){
                            temp=nums[j];
                            nums[j]=nums[j+1];
                            nums[j+1]=temp;
                        }
                    }
                }
                
            }
            }
        }
    }

格雷编码

class Solution {
        List<Integer> res = new ArrayList<>();
      public List<Integer> grayCode(int n) {
       dfs(n,new StringBuffer(),new int[]{0,1});
        return res;
    }

    /**
     *
     * @param n 层数
     * @param stringBuilder
     *
     */
    public void dfs(int n, StringBuffer sb, int[] nums){
 
        if(sb.length() == n){
        
            res.add(Integer.valueOf(sb.toString(),2));
            return;
        }
        sb.append(nums[0]);
        dfs(n,sb,new int[]{0,1});
        sb.deleteCharAt(sb.length()-1);
        sb.append(nums[1]);
        dfs(n,sb,new int[]{1,0});
        sb.deleteCharAt(sb.length()-1);
    }
}

死锁

Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
    synchronized (A) {
        log.debug("lock A");
        sleep(1);
        synchronized (B) {
            log.debug("lock B");
            log.debug("操作...");
        }
    }
}, "t1");
Thread t2 = new Thread(() -> {
    synchronized (B) {
        log.debug("lock B");
        sleep(0.5);
        synchronized (A) {
            log.debug("lock A");
            log.debug("操作...");
        }
    }
}, "t2");
t1.start();
t2.start();

活锁

public class TestLiveLock {
    static volatile int count = 10;
    static final Object lock = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            // 期望减到 0 退出循环
            while (count > 0) {
                sleep(0.2);
                count--;
                log.debug("count: {}", count);
            }
        }, "t1").start();
        new Thread(() -> {
            // 期望超过 20 退出循环
            while (count < 20) {
                sleep(0.2);
                count++;
                log.debug("count: {}", count);
            }
        }, "t2").start();
    }
}

你可能感兴趣的:(leetcode,排序算法,数据结构)