【Java数据结构与算法笔记(一)】常见排序算法及面试考点总结

一切为了暑期实习,一切为了暑期实习。
我就是试着去面面,实习是不可能实习的,老板是不会让我们去的。
哭!!!
以下是正题!!!
2019年3月11日:突然想做成一个整理笔记了,那这个就作为开篇吧~


以下排序排名有先后之分,请注意复习策略~

快速排序

快排特别重要!!!
快排特别重要!!!
快排特别重要!!!

基本思想

  1. 首先在数组中选择一个基准点(一般选取第一个数字),
  2. 然后分别从数组的两端扫描数组,设两个指示标志(low指向起始位置,high指向末尾),
    ( 首先从后半部分开始,如果发现有元素比该基准点的值小,就交换low和high位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换low和high位置的值,如此往复循环,直到low>=high,然后把基准点的值放到high这个位置。一次排序就完成了。)
  3. 以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。

代码实现(递归➕非递归)

递归
package algorithm;

public class QuickSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97};
		int low = 0, high = arr.length-1;
		quickSort(arr,low,high);
		
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void quickSort(int[] arr, int low, int high) {
		if(low >= high) return;
		int index = partition(arr,low,high);
		quickSort(arr,low,index-1);
		quickSort(arr,index+1,high);
	}
	public static int partition(int[] arr, int low, int high) {
		int key = arr[low];
		while(low < high) {
			while(low < high && arr[high] >= key) high --;
			arr[low] = arr[high];
			while(low < high && arr[low] <= key) low ++;
			arr[high] = arr[low];
		}
		arr[high] = key;
		return high;
	}
}
非递归
package leetcoode;

import java.util.Stack;
public class QuickSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97};
		int low = 0, high = arr.length-1;
		quickSort(arr,low,high);
		
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void quickSort(int[] arr, int low, int high) {
		if(low >= high) return;
		
		Stack<Integer> stack = new Stack<Integer>();
		if(low < high) {
			stack.push(low);
			stack.push(high);
			while(!stack.isEmpty()) {
				int right = stack.pop();
				int left = stack.pop();
				int pivot = partition(arr,left,right);
				if(pivot-1 > left) {
					stack.push(low);
					stack.push(pivot-1);
				}
				if(pivot+1 < right) {
					stack.push(pivot+1);
					stack.push(right);
				}
			}
		}
	}
	public static int partition(int[] arr, int low, int high) {
		int key = arr[low];
		while(low < high) {
			while(low < high && arr[high] >= key) high --;
			arr[low] = arr[high];
			while(low < high && arr[low] <= key) low ++;
			arr[high] = arr[low];
		}
		arr[high] = key;
		return high;
	}
}

快排的改进思路

详细的看这里:https://blog.csdn.net/hacker00011000/article/details/52176100

我看有的面经上有问到这个,赶紧总结下:
(回答的时候别给自己挖坑,选自己能实现的回答)

  1. 优化1:当待排序序列的长度分割到一定大小后,使用插入排序;
  2. 优化2:在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割;
  3. 优化3:使用尾递归优化递归操作;
  4. 优化4:使用并行或多线程处理子序列。

冒泡排序

基本思想

相邻两节点进行比较,大的向后移一个,经过第一轮两两比较和移动,最大的元素移动到了最后,第二轮次大的位于倒数第二个,依次进行。

代码实现

package algorithm;

public class BubbleSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
		bubbleSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void bubbleSort(int[] arr) {
		for(int i=0;i<arr.length-1;i++) {
			for(int j=0;j<arr.length-1-i;j++) {
				if(arr[j]>arr[j+1]) {
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
	}
}

堆排序

基本思想

堆排序我觉得可能是最复杂的一种排序方式了,涉及了堆、完全二叉树的一些性质,就不细说了,大家参考下面这篇文章,掌握主要流程,重点还是在调整堆这一块。
https://blog.csdn.net/u013384984/article/details/79496052

代码实现

public class HeapSort {
	public static void main(String[] args) {
		int[] arr={4,1,3,7};
		heapSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	public static void swap(int[] arr, int i, int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	public static void heapSort(int[] arr){
		// 从第一个非叶节点自下往上进行调整
		for(int i=arr.length/2-1;i>=0;i--)
			adjustHeap(arr,i,arr.length);
		
		// 交换堆顶元素和最后一个元素,调整堆
		for(int i=arr.length-1;i>=0;i--) {
			swap(arr,0,i);
			adjustHeap(arr,0,i);
		}
	}
	public static void adjustHeap(int[] arr, int i, int length){
		int temp = arr[i];
		for(int k=2*i+1;k<length;k=k*2+1) {
			if(k+1<length && arr[k]<arr[k+1])
				k++;
			if(arr[k]>temp) {
				arr[i] = arr[k];
				i = k;
			}
		}
		arr[i] = temp;
	}
}

直接插入排序

基本思想

遍历数组,遍历到i时,a0,a1…ai-1是已经排好序的,取出ai,从ai-1开始向前和每个比较大小,如果小于,则将此位置元素向后移动,继续先前比较,如果不小于,则放到正在比较的元素之后。可见相等元素比较是,原来靠后的还是拍在后边,所以插入排序是稳定的。

代码实现

package algorithm;

public class InsertSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97,76,13,27,49,78};
		insertSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	public static void insertSort(int[] arr) {
		for(int i=1;i<arr.length;i++) {
			int temp = arr[i];
			int j=i-1;
			while(j>=0 && arr[j]>temp) {
				arr[j+1] = arr[j];
				j--;
			}
			arr[j+1] = temp;
		}
	}
}

归并排序

基本思想

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

代码实现

package algorithm;

public class MergeSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97,38};
		int low = 0, high = arr.length-1;
		mergeSort(arr,low,high);
		
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void mergeSort(int[] arr, int low, int high) {
		if(low < high) {
			int mid = (low+high)/2;
			mergeSort(arr,low,mid);
			mergeSort(arr,mid+1,high);
			merge(arr,low,mid,high);
		}
	}
	public static void merge(int[] arr, int low, int mid, int high) {
		int[] temp = new int[high-low+1];
		int k=0,i=low,j=mid+1;
		while(i<=mid && j<=high) {
			if(arr[i]<arr[j]) 
				temp[k++] = arr[i++];
			else
				temp[k++] = arr[j++];
		}
		while(i<=mid)
			temp[k++] = arr[i++];
		while(j<=high)
			temp[k++] = arr[j++];
		
		for(k=0;k<temp.length;k++) {
			arr[low+k] = temp[k];
		}
	}
}

简单选择排序

package algorithm;

public class SelectSort {
	public static void main(String[] args) {
		int[] arr={4,1,3,7};
		selectSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	public static void selectSort(int[] arr) {
		for(int i=0;i<arr.length;i++) {
			int index=i;
			for(int j=i+1;j<arr.length;j++)
				if(arr[index]>arr[j]) 
					index = j;
			int temp = arr[index];
			arr[index] = arr[i];
			arr[i] = temp;
		}
	}
}

各种排序算法比较(table)

【Java数据结构与算法笔记(一)】常见排序算法及面试考点总结_第1张图片

问题1: 稳定性

  1. 什么是排序算法的稳定性(摘自百度百科)

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

  1. 对于不稳定的排序算法,只要举出反例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而得到稳定的特性。
  2. 快(快排)些(希尔)选(简单选择)一堆(堆排序)这四个都是不稳定的,可以按照
    快些选一堆
    快些选一堆
    快些选一堆
    这个顺口溜记,我觉得还是很方便的,考验之后两年多了,都没忘!!

问题2: 空间复杂度

两种算法需要特殊记:

  1. 快排:由于需要一个辅助栈来实现递归,空间复杂度平均为O(log2n) ,最坏情况下为O(n)
  2. 归并排序:在合并操作中需要借助较多的辅助空间用于元素复制,所以为O(n)
  3. 其他算法都为O(1)

问题3: 时间复杂度

大部分参照上面的table即可,但是需要特别注意快排,我在四场面试问到过两次

  1. 平均时间复杂度为O(nlog2n),但是在元素基本有序的时候反而性能最低为O(n)
  2. 快排被认定为目前内部排序性能最好的算法

问题4: 那种算法可以做到线性时间复杂度?

计数、桶、基数
相关证明看这个文章:https://www.jianshu.com/p/15439561706d


以上,差不多可以应付面试了,如果有问题及时反馈给我哦~

你可能感兴趣的:(java,面试,排序算法,求职,Java和Spark)