最近因为面试,都没好好学习算法,将冒泡排序、直接插入排序、希尔排序、快速排序、堆排序、归并排序、基数排序、计数排序、桶排序写了一下
一、冒泡排序
/**
* Created by april on 2018/8/3.
* 冒泡排序O(n的平方)
*/
public class Bubble_sort
{
public static void main(String[] args){
int [] arr = {1,1,2,0,9,3,12,7,8,3,4,65,22};
BubbleSort_3(arr,arr.length);
for(int i: arr){
System.out.print(i+",");
}
}
public static void BubbleSort_1(int[] a ,int n){
for(int i=0; ia[j]){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
/*优化1:
1、交换n遍,每一遍相邻交换,则把最大的放在了后面
2、设置flag,排序一遍减去尾边界
* */
public static void BubbleSort_2(int[] a, int n){
boolean flag = true;
int k = n;
while(flag){
flag = false;
for (int j=0;ja[j+1]){//判断相邻结果的大小
int temp;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = true;
}
}
k--;//减少一次排序的尾边界
}
}
/*优化2:
现在有一个包含1000个数的数组,仅前面100个无序,后面900个都已排好序且都大于前面100个数字
1、只需要有一次比较后面的900个数据,之后就会设置尾边界,保证后面的900个数据不再被排序
2、再按BubbleSort_2进行排序
* */
public static void BubbleSort_3(int[] a, int n){
int k;
int flag = n;
while(flag > 0 ){//排序未结束的标志
k = flag;// k存遍历的尾边界
flag = 0;
for(int j = 0; j< k-1; j++){
if(a[j]>a[j+1]){
int temp;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = j+1;//重新设置尾边界
}
}
}
}
}
二、直接插入排序
/**
* Created by april on 2018/8/3.
* 直接插入排序
* O(n的平方)
*/
public class Straight_insertion
{
public static void main(String[] args){
int[] arr = {1,1,2,0,9,3,12, 7, 8, 3, 4, 65, 22};
Straight_insertion.insertSort_3(arr,arr.length);
for(int i : arr){
System.out.print(i+",");
}
}
/*
找关键字
将a[i]并入当前的有序区a[0...i-1]中形成a[0...i]的有序区间
i++并重复第二步直到i=n-1,排序完成
* */
public static void insertSort_1(int[] a, int n){
int i,j;
for(i=1; ia[i]) break;//如果a[j]比a[i]大,求得a[i]的位置,应该放在a[j]前
}
//将a[i]插入到a[j]的位置
int temp = a[i];//找到a[i]的数据
for(int k = i-1; k>=j;k--){//a[j]到a[i-1]从后往前后移
a[k+1] = a[k];
}
a[j] = temp;//将a[i]插入到a[j]前
}
}
/*
优化1:
从第一个开始比较,a[i]只与a[i-1]比较,如果a[i-1]a[i],边比较边移动,j=i-1,将a[j]往后移动一边向前搜索是否还有比a[i]大的
* */
public static void insertSort_2(int[] a, int n){
int i,j;
for(i=1; ia[i-1]){
int temp = a[i];//保存要插入的数据
for(j=i-1;j>=0&&a[j]>temp;j--){
a[j+1] = a[j];//将i-1往后移动
}
//插入数据
a[j+1] = temp;
}
}
}
/*优化2:
1、a[i]与前一个a[i-1]交换,并且如果比前面的小,一直和之前的交换
* */
public static void insertSort_3(int[] a, int n){
int i,j;
for(i=1;i=0 && a[j]>a[j+1]; j--){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
三、希尔排序
/**
* Created by april on 2018/8/8.
* 希尔排序:插入排序,缩小增量排序,比O(n的平方小)
* 注:希尔排序的增量序列选择与证明是难题
* 建议的增量排序,希尔增量:gap = length/2,gap = gap/2
* 思考: 先选择gap = length/2的增量,分为若干组进行排序
* 继续gap = gap/2
* 当gap=1,对整个序列进行一趟直接插入排序
*
*/
public class Shell_sort
{
public static void main(String[] args){
int[] arr = {49,38,65,97,76,13,27,49,55,04};
System.out.println(Arrays.toString(arr));
move_sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void move_sort(int[] arr){
for(int gap = arr.length/2;gap>0;gap/=2){//增量gap,最后的gap一定是1
for(int i = gap;i=0&&temp
四、快速排序
/**
* Created by april on 2018/8/3.
* 快速排序,不稳定,最差是O(n的地方),平均时间复杂度是O(nlogn),用主定理分析
*/
public class Quick_sort
{
public static void main(String[] args){
// int[] arr = {49,38,65,97,76,13,27,49};
// Quick_sort.Quick_1(arr,0,arr.length-1);
// for(int i:arr){
// System.out.print(i+",");
// }
//输入链表 2 2 5 3 8 4 2 1
ListNode head = new ListNode(2);
ListNode l1 = new ListNode(2);
ListNode l2 = new ListNode(5);
ListNode l3 = new ListNode(3);
ListNode l4 = new ListNode(8);
ListNode l5 = new ListNode(4);
ListNode l6 = new ListNode(2);
ListNode l7 = new ListNode(1);
//将链表连接起来
head.next = l1;
l1.next = l2;
l2.next = l3;
l3.next = l4;
l4.next = l5;
l5.next = l6;
l6.next = l7;
l7.next = null;
ListNode p = head;
while (p.next != null){
System.out.print(p.val+" ");
p = p.next;
}
//要得到最后一个数,所以需要单独打印
System.out.println(p.val);
ListNode begin = head, end = p;
Quick_ListNode(begin,end);//第一个和后一个,要用于判断是否为空,还有链表中是否只有一个数
p = head;
while (p!=null){
System.out.print(p.val+" ");
p = p.next;
}
}
/*一趟快排
(1)先选取基准点key,一般选a[0],从后半部分开始扫描,若比key值大则继续往前移动,若比key值小,则交换a[low]和a[high],此时a[low]为key
(2)在从前半部分开始扫描,若比Key值小则继续往后移动,若比key值大,则交换a[low]和a[high],因为之前的a[low]已经保存了上次的a[high],所以现在a[high]=现在a[low]
(3)获取划分点
* */
public static int One_Quick(int[] a,int low, int high){
int key=a[low];
while(lowkey)//while里必须加上low=high) return;
//获取划分点
int index = One_Quick(a,low,high);
//对前半部分排序
Quick_1(a,low,index-1);
//对后半部分排序
Quick_1(a,index+1,high);
}
/*单链表实现快排,思想还是把小于key值的放在左边,大于key值的放在右边
1、两个指针,first和second,first从头开始,second从first.next开始,key值选为first;
2、由second开始遍历,当second>key,则first->first.next,swap(first,second) 3、继续second->second.next,直到second到链表末尾
4、交换头结点和first的值,完成一趟快排
5、再使用递归完成快排
* */
public static void Quick_ListNode(ListNode begin,ListNode end){
if(begin == null || end == null || begin==end){
return;
}
ListNode first = begin;
ListNode key = first;
ListNode second = begin.next;
while(second != end.next && second != null){//当second遍历到链表末尾
if(second.val>key.val){
first=first.next;
swap(first,second);
}
second = second.next;
}
if(begin != first)
{
swap(begin,first);
}
Quick_ListNode(begin,first);
Quick_ListNode(first.next,end);
}
public static void swap(ListNode first,ListNode second){
int temp;
temp = first.val;
first.val = second.val;
second.val = temp;
}
}
五、堆排序
/**
* Created by april on 2018/8/7.
* 堆排序
* 思想:
* 将待排序序列构造一个大顶堆,整个序列的最大值就是堆顶的根节点
* 将其与末尾元素进行交换,此时末尾就为最大值
* 将剩余n-1个元素重新构造成一个堆,就会得到n个元素的次小值
* 反复得到一个有序序列
*/
public class Heap_sort
{
public static void main(String[] args){
int[] arr = {70,60,12,40,30,8,10};
heap_sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heap_sort(int[] arr){
//构建大顶堆
for(int i = arr.length/2-1; i>=0; i--){
adjust_heap(arr,i,arr.length);//从第一个非叶子结点从下至上,从右到左调整结构
}
//调整堆结构,交换堆顶元素与末尾元素
for(int j = arr.length-1; j>0;j--){
swap(arr,0,j);
adjust_heap(arr,0,j);//重新对堆进行调整
}
}
//调整堆,在大顶堆已构建的情况下
public static void adjust_heap(int[] arr, int i, int length){
int temp = arr[i];
for(int k = i*2+1;ktemp){//如果子节点大于父节点,将子节点复制给右节点(不用交换),因为事先已经将父节点取出来了
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放在最终的位置
}
//交换元素
public static void swap(int[] arr,int a,int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
六、归并排序
/**
* Created by april on 2018/8/4.
* 归并排序 O(nlogn)
* 将两个已排序的表合并成一个表
* 先递归拆分序列,再归并
*/
public class Merge_sort
{
public static void main(String[] args){
//数组
// int[] arr = {49,38,65,97,76,13,27};
// Merge_sort.sort(arr);
// System.out.println(Arrays.toString(arr));
//链表
ListNode head = new ListNode(0);
ListNode l1 = new ListNode(2);
ListNode l2 = new ListNode(5);
ListNode l3 = new ListNode(3);
ListNode l4 = new ListNode(8);
ListNode l5 = new ListNode(4);
ListNode l6 = new ListNode(2);
ListNode l7 = new ListNode(1);
//将链表连接起来
head.next = l1;
l1.next = l2;
l2.next = l3;
l3.next = l4;
l4.next = l5;
l5.next = l6;
l6.next = l7;
l7.next = null;
ListNode p = head;
while (p.next != null){
System.out.print(p.val+" ");
p = p.next;
}
//要得到最后一个数,所以需要单独打印
System.out.print(p.val);
System.out.println();
new Merge_sort().merge_sort(head);
p = head;
while(p!=null){
System.out.print(p.val+" ");
p = p.next;
}
}
/*一、基于数组实现,先把数组对半划分,再对半划分的数组排序,从而使左右两个子数组各自有序,最后再将两排序号的子数组进行归并成一个大的有序数组*/
//排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
public static void sort(int[] arr){
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
}
//递归拆分、二路归并,
// 决
public static void mergeSort(int[] a, int first, int last, int[] temp){
if(first < last){
int mid = (first+last)/2;
mergeSort(a,first,mid,temp);//左边有序
mergeSort(a,mid+1,last,temp);//右边有序
mergeArray(a,first,mid,last,temp);//再将两个有序序列合并
}
}
//归并两个有序序列,a[first,mid]和a[mid+1,end]
private static void mergeArray(int[] a, int first, int mid, int last, int[] temp){
int i = first, j = mid+1;//设置两个数字的起始边界
//int m = mid, n = last;//设置两个数组的结束边界
int k=0;//temp的指针数组
while(i <= mid && j <= last){
if(a[i] <= a[j]){
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
while(i<=mid){
temp[k++] = a[i++];
}
while (j<=last){
temp[k++] = a[j++];
}
k = 0;
while(first<=last){
a[k++] = temp[k++];//将temp中的元素全部拷贝到原数组中,最后输出原数组
}
}
/*二、基于链表实现
* (1)合并:对于两个有序单链表的合并并返回合并之后的单链表头结点
* (2)拆分,使用双指针法,p1和p2分别往后移动,p1移动一次,p2移动两次,当p2移动到尾结点时,p1就指向中间结点
* */
//拆分
public ListNode merge_split(ListNode head){
if(head == null) return head;
ListNode p1 = head;
ListNode p2 = head;
while(p2.next != null && p2.next.next != null){//当p2的next为空的时候,p2为尾结点,且p2要移动两步
p1 = p1.next;//p1走一步
p2 = p2.next.next;//p2走两步
}
return p1;//此时p1为中间结点
}
//合并
public ListNode merge(ListNode first,ListNode second){
//
ListNode dummyHead = new ListNode(0);
ListNode curr = dummyHead;
while(first != null && second != null){//比较两个子序列
if(first.val <= second.val){
curr.next = first;//放入较小的
first = first.next;
}else{
curr.next = second;
second = second.next;
}
curr = curr.next;
}
curr.next = (first == null) ? second : first;
return dummyHead.next;
}
//单链表的归并排序
public ListNode merge_sort(ListNode head){
//[i...middle]和[middle+1...n]
if(head == null || head.next == null) return head;
ListNode middle = merge_split(head);
ListNode shalf = middle.next;//
middle.next = null;//拆分链表
return merge(merge_sort(head),merge_sort(shalf));
}
}
七、基数排序
/**
* Created by april on 2018/8/8.
* 基数排序
*
* 基本概念:
* LSD:短的关键字被认为是小的,排在前面,然后相同长度的关键字再按照词典顺序或者数字大小等进行排序。比如1,2,3,4,5,6,7,8,9,10,11或者”b, c, d, e, f, g, h, i, j, ba” 。
* MSD:直接按照字典的顺序进行排序,对于字符串、单词或者是长度固定的整数排序比较合适。比如:1, 10, 2, 3, 4, 5, 6, 7, 8, 9和 “b, ba, c, d, e, f, g, h, i, j”。
*
*基数排序思想:从低位开始将待排序的数按照这一位的值放到相应的编号0~9的桶中,等到低位排完得到一个子序列,再将这个序列按照次低位的大小进入相应的桶中,一直排到最高位为止,数组排序完成
举例:若数组为{73,22,93,43,55,14,28,65,39,81}
用一个二维数组bucket[10][arr.length]来保存桶的结构,行为0~9的桶,列为桶里的个数(排序结果相同放在一个桶里)
0 1 2 3 4 5 6 7 8 9
1、按第一位排序
0 1 2 3 4 5 6 7 8 9
81 22 73 14 55 28 39
93 65
43
可得按个位排序的结果是{81,22,73,93,43,14,55,65,28,39}
2、按第二位排序
0 1 2 3 4 5 6 7 8 9
14 22 39 43 55 65 73 81 93
28
取出排序结果{14,22,28,39,43,55,65,73,81,93}
*/
public class Radix_sort
{
public static void main(String[] args){
int[] arr = {73,22,93,43,55,14,28,65,39,81};
System.out.println(Arrays.toString(arr));
radix_sort(arr);
System.out.println(Arrays.toString(arr));
}
//要求比最大的值的位数还要大1位,如题目中d应该为100
public static int getMaxDigit(int[] arr){
int d = 10;
for(int i = 0; i=d){
d *= 10;
}
}
return d;
}
public static void radix_sort(int[] arr){
int d = getMaxDigit(arr);
int n = 1;
int k = 0;//保存每一次排序的结果,保存在原来arr数组
int[][] bucket = new int[10][arr.length];//桶,存储排序结果
int[] order = new int[arr.length];//存储桶的每列有多少个数,从0开始
while(n
八、计数排序
/**
* Created by april on 2018/8/8.
* 计数排序:知道数组里有多少项小于或等于该元素,就能给出该元素在排序后的数组的位置(第3步就是做这件事情)
* 例如A={2,5,3,0,2,3,0,3}
* 1、原数组A: 0 1 2 3 4 5 6 7
2 5 3 0 2 3 0 3
* 2、数组里最大值为5,则初始化C的大小为6,原数组里有2个0,0个1,2个2,3个3,0个4,1个5.
C:0 1 2 3 4 5
2 0 2 3 0 1
*3、将C中每个i位置的元素大小改为C数组前i项和
C:0 1 2 3 4 5
2 2 4 7 7 8
*4、初始化和A一样大小的B用来存排序后的数组,倒序遍历A,为了稳定性(即原数组中A的三个3相对距离不变)
(1)i=7时,a[i]=3,c[3]=7,b[7-1]=b[6]=3,c[3]=c[3]-1
B: 0 1 2 3 4 5 6 7 C: 0 1 2 3 4 5
3 2 2 4 6 7 8
(2)i=6时,a[i]=0,c[0]=2,b[2-1]=b[1]=0,c[0]=c[0]-1
B: 0 1 2 3 4 5 6 7 C: 0 1 2 3 4 5
0 3 1 2 4 6 7 8
......
*/
public class Count_sort
{
public static void main(String[] args){
int[] arr = {2,5,3,0,2,3,0,3};
System.out.println(Arrays.toString(arr));
int max = getMax(arr);
int[] B = count_sort(arr,max);//用B存排序后的数组
System.out.println(Arrays.toString(B));
}
//得到原数组里的最大值,用来构造C
public static int getMax(int[] arr){
int max = arr[0];
for(int i=0;i=0;i--){//倒序遍历A
B[C[arr[i]]-1] = arr[i];//B存排序后的数组
C[arr[i]]--;
}
return B;
}
}
九、桶排序
/**
* Created by april on 2018/8/8.
* 桶排序:O(N+n),空间换时间
*
*简单入门:
* O(桶的个数+待排序的个数)
* 先给一个数组(即为桶),全部赋值为0
* 每出现一个值,对应数组就加1(即对应的桶里放一个小旗子)
* 再依次判断数组里不为空的值,出现几次(有多少个小旗子)就打印几次
*
*桶排序:将[0,1)区间划分为n个相同的大小的子区间,这些子区间被称为桶
然后将n个输入元素分别放入各自的桶中,因为输入时均匀独立的,一般不会有很多数同时落在一个桶中的情况
要对各个桶中的数据进行排序,然后遍历各个桶,按照次序把各个桶中的元素列出来即可
举例:
A B
78 0
17 1 :12 :17
39 2 :21 :23 :26
26 3 :39
72 4
94 5
21 6 :68
12 7 :72 :78
23 8
68 9 :94
*/
public class Bucket_sort
{
public static void main(String[] args){
int[] arr = {78,17,39,26,72,94,21,12,23,68};
System.out.println(Arrays.toString(arr));
buck_sort(arr);
System.out.println(Arrays.toString(arr));
/*桶排序简单入门*/
// int[] book = new int[101];
// for(int i = 0; i<=100; i++){
// book[i] = 0;
// }
// for(int i = 0;i=0;i--){//由大到小输出
// for(int k = 1;book[i]>=k;k++){//输出至少桶里为1个的数,桶里的数有几个就输出几次
// System.out.print(i+" ");
// }
}
public static void buck_sort(int[] arr){
int bucknum = arr.length;
List> list = new ArrayList<>(bucknum);
for(int i = 0; i());
//确定元素的最值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int a : arr){
max = Math.max(max,a);
min = Math.min(min,a);
}
//确定每个元素对应桶的编号并放进去
for(int a : arr){
int index = (int)((a - min)/(max+min+1.0)*arr.length);//加1为了防止程序抛出IndexOutOfBoundsEx
list.get(index).add(a);
}
//桶内排序
for(int i = 0; i A : list){
for (int a : A){
arr[k++] = a;
}
}
}
}