冒泡排序
排序思想
就像“冒泡泡”一样,从头开始,两个两个数作比较,将两个数中较大(较小)的数向后移,再比较下面两个数,依次将较大(较小)的数向后移,最终,将一组数中最大(最小)的数,移动到最后面,再从头开始,移下一个数
下面是一个栗子:
要排序数组(升序):{4,2,7,3,1}
————————————
第一趟(交换四次):
- 第一次:4与2比较,4大于2,交换两个数——{2,4,7,3,1}
- 第二次:4与7比较,7大于4,不交换位置——{2,4,7,3,1}
- 第三次:7与3比较,7大于3,交换两个数——{2,4,3,7,1}
- 第四次:7与1比较,7大于1,交换两个数——{2,4,3,1,7}
第二趟(交换3次):
- 第一次:2与4比较,4大于2,不交换位置——{2,4,3,1,7}
- 第二次:4与3比较,4大于3,交换两个数——{2,3,4,1,7}
- 第三次:4与1比较,4大于1,交换两个数——{2,3,1,4,7}
第三趟(交换两次):
- 第一次:2与3比较,3大于2,不交换位置——{2,3,1,4,7}
- 第二次:3与1比较,3大于1,交换两个数——{2,1,3,4,7}
第四趟(交换一次):
- 第一次:2与1比较,2大于1,交换两个数——{1,2,3,4,7}
————————————
根据上面的栗子可以得出有n个数字要进行排序总共要进行 n - 1 趟,每 i 趟要排序 n - i - 1 次
排序优点
每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,每进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。
时间复杂度
冒泡排序的时间复杂度为O(N^2)
空间复杂度
冒泡排序的空间复杂度为O(1)
实现代码
/**
* 冒泡排序(升序)
* @author LXY
* @email [email protected]
* @date 2018/8/18 16:10
*/
public class BubbleSort {
public static void bubbleSort(int[] arr)
{
if(arr == null || arr.length == 0)
{
return;
}
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])
{
swap(arr,j,j + 1);
}
}
}
}
private static void swap(int[] arr, int j, int i) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void main(String[] args) {
int[] arr = {4,2,7,3,1,8,9,5};
bubbleSort(arr);
for(int i:arr)
{
System.out.print(i + " ");
}
}
}
选择排序
排序思想
每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。也就是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。
下面是一个栗子:
要排序数组(升序):{4,2,7,3,1}
————————————
第一趟:
- 将数组中最小的元素1与第一个元素4交换——{1,2,7,3,4}
第二趟:
- 将除了第一个元素外的最小元素2,与第二个位置的元素2交换(此处就是2,因此不用交换)——{1,2,7,3,4}
第三趟:
- 将除了前两个元素外的最小元素3与第三个位置的元素7交换——{1,2,3,7,4}
第四趟:
- 将除了前三个元素外的最小元素4与第四个位置的元素7交换——{1,2,3,4,7}
————————————
时间复杂度
选择排序的时间复杂度为O(N^2)
空间复杂度
选择排序的空间复杂度为O(1)
代码实现
package selectSort;
/**
* 选择排序(升序)
* @author LXY
* @email [email protected]
* @date 2018/8/18 16:31
*/
public class SelectSort {
public static void selectSort(int[] arr)
{
if(arr == null || arr.length == 0)
{
return;
}
for(int i = 0;i < arr.length - 1;i++)
{
int index = i;
for(int j = i + 1;j < arr.length;j++)
{
if(arr[j] < arr[index])
{
swap(arr,j,index);
}
}
}
}
private static void swap(int[] arr, int j, int i) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void main(String[] args) {
int[] arr = {4,2,7,3,1,8,9,5};
selectSort(arr);
for(int i:arr)
{
System.out.print(i + " ");
}
}
}
插入排序
排序思想
插入排序就像是玩扑克牌时要把牌放到手中以有的牌中一样,像下面这张图一样,原本手中的牌是{2,4,5,10},但是抽到了一张7,此时应该怎么办?
将牌从右向左看,7比10小,比5大,那么正好就放到这里,那么为什么不继续向前面比较了?是因为牌本身是有顺序的
图片是盗的
下面是一个情况最坏的栗子:
要排序数组(升序):{5,4,3,2}
————————————
第一次插入:
- 将位置1(数组从位置0开始算起,并且默认位置0,即第一个元素已经插入完成)的元素4开始与它之前的元素5进行比较,发现4比5小,将4和5交换{4,5,3,2}
第二次插入:
- 将位置2的元素3与它之前的元素5进行比较,发现3比5小,将3和5交换{4,3,5,2}
- 将位置1的元素3与它之前的元素4进行比较,发现3比4小,将3和4交换{3,4,5,2}
第三次插入:
- 将位置3的元素2与它之前的元素5进行比较,发现2比5小,将2和5交换{3,4,2,5}
- 将位置2的元素2与它之前的元素4进行比较,发现2比4小,将3和5交换{3,2,4,5}
- 将位置1的元素2与它之前的元素3进行比较,发现2比3小,将2和3交换{2,3,4,5}
————————————
时间复杂度
插入排序的时间复杂度为O(N^2)
空间复杂度
插入排序的空间复杂度为O(1)
代码实现
package insertSort;
/**
* 插入排序(升序)
* 该种方法需要不断地进行交换赋值,这样会产生时间地浪费
* @author LXY
* @email [email protected]
* @date 2018/8/18 16:48
*/
public class InsertSort {
public static void insertSort(int[] arr)
{
if(arr == null || arr.length == 0)
{
return;
}
//默认第一个位置的元素摆放完毕,从第二个位置的元素开始
for(int i = 1;i < arr.length;i++)
{
for(int j = i;j > 0 && arr[j] < arr[j - 1];j--)
{
swap(arr,j,j - 1);
}
}
}
private static void swap(int[] arr, int j, int i) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void main(String[] args) {
int[] arr = {4,2,7,3,1,8,9,5};
insertSort(arr);
for(int i:arr)
{
System.out.print(i + " ");
}
}
}
package insertSort;
/**
* 插入排序(升序)
* 该种方法是上面那种方法地改进版本,不需要来回地交换元素
* @author LXY
* @email [email protected]
* @date 2018/8/18 16:48
*/
public class InsertSort2 {
public static void insertSort(int[] arr)
{
if(arr == null || arr.length == 0)
{
return;
}
//默认第一个位置的元素摆放完毕,从第二个位置的元素开始
for(int i = 1;i < arr.length;i++)
{
int j;
int tmp = arr[i];
for(j = i;j > 0 && arr[j - 1] > tmp;j--)
{
arr[j] = arr[j - 1];
}
arr[j] = tmp;
}
}
private static void swap(int[] arr, int j, int i) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void main(String[] args) {
int[] arr = {4,2,7,3,1,8,9,5};
insertSort(arr);
for(int i:arr)
{
System.out.print(i + " ");
}
}
}
快速排序
排序思想
以一个值为基准(通常以第一个值),然后将比他小的数移到它左边,比他大的数移到右边
详细见博客:http://developer.51cto.com/art/201403/430986.htm
时间复杂度
快速排序的时间复杂度平均为O(N*logN)
在最好情况下,每次划分所取的基准都是当前无序区的”中值”记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(N*logN)快速排序的时间复杂度最坏为O(N^2)
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。因此,快速排序必须做nN1次划分,第i次划分开始时区间长度为N-i+1,所需的比较次数为N-i(1≤i≤N-1),故总的比较次数达到最大值:Cmax= N*(N-1)/2=O(N^2)
空间复杂度
- 快速排序的空间复杂度为O(logN)
快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为O(logN),故递归后需栈空间为O(logN)
代码实现
递归实现
package quickSort;
/**
* 快速排序(递归实现)
* @author LXY
* @email [email protected]
* @date 2018/8/18 17:28
*/
public class QuickSort {
public static void quickSort(int[] arr,int start,int end)
{
if(arr == null || arr.length == 0 || start > end)
{
return;
}
int key = quickSortHelp(arr,start,end);
quickSort(arr,start,key - 1);
quickSort(arr,key + 1,end);
}
//找中间节点位置的辅助函数
private static int quickSortHelp(int[] arr, int start, int end) {
if(arr == null || arr.length == 0 || start > end)
{
return -1;
}
int keyValue = arr[start];
while (start < end)
{
while (start < end && arr[end] > keyValue)
{
//找到第一个小于keyValue的元素
end--;
}
//交换两个元素
arr[start] = arr[end];
while (start < end && arr[start] <= keyValue)
{
//找到第一个大于keyValue的元素
start++;
}
arr[end] = arr[start];
}
arr[start] = keyValue;
return start;
}
public static void main(String[] args) {
int[] arr = {4,2,7,3,1,8,9,5};
quickSort(arr,0,arr.length - 1);
for(int i:arr)
{
System.out.print(i + " ");
}
}
}
非递归实现
package quickSort;
import java.util.Stack;
/**
* 快速排序(非递归实现)
* @author LXY
* @email [email protected]
* @date 2018/8/18 17:28
*/
public class QuickSortRec {
public static void quickSort(int[] arr,int start,int end)
{
if(arr == null || arr.length == 0 || start > end)
{
return;
}
Stack<Integer> stack = new Stack<>();
if(start < end)
{
stack.push(end);
stack.push(start);
while (!stack.isEmpty())
{
int left = stack.pop();
int right = stack.pop();
int key = quickSortHelp(arr,left,right);
if(left < key)
{
//出栈元素在标记位置左边
//将标记位置和刚才出栈的元素依次入栈
stack.push(key - 1);
stack.push(left);
}
if(right > key)
{
//出栈元素在标记位置右边
//将刚才出栈的元素和标记位置的元素依次入栈
stack.push(right);
stack.push(key + 1);
}
}
}
}
//找中间节点位置的辅助函数
private static int quickSortHelp(int[] arr, int start, int end) {
if(arr == null || arr.length == 0 || start > end)
{
return -1;
}
int keyValue = arr[start];
while (start < end)
{
while (start < end && arr[end] > keyValue)
{
//找到第一个小于keyValue的元素
end--;
}
//交换两个元素
arr[start] = arr[end];
while (start < end && arr[start] <= keyValue)
{
//找到第一个大于keyValue的元素
start++;
}
arr[end] = arr[start];
}
arr[start] = keyValue;
return start;
}
public static void main(String[] args) {
int[] arr = {4,2,7,3,1,8,9,5};
quickSort(arr,0,arr.length - 1);
for(int i:arr)
{
System.out.print(i + " ");
}
}
}