排序算法
排序算法总览
(图片百度滴昂…)
1 选择排序
选一个数和数据进行比较:
int[] nums = { 32, 13, 45, 22 };
for(int i = 1; i < nums.length; i++) {
for(int j = 0; j < nums.length; j++) {
if(nums[i] > nums[j]) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
}
for(int num : nums) {
System.out.print(num + " ");
}
2 冒泡排序
相邻两个元素的比较:
int[] nums = { 32, 13, 45, 22 };
for(int i = 0; i < nums.length-1; i++) {
for(int j = 0; j < nums.length-1-i;j++) {
if(nums[j] > nums[j+1]) {
int temp;
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
for(int num : nums) {
System.out.print(num + " ");
}
例子:现有一堆英文字母的歌曲,要求按照首字母排序:
// 对英文歌曲的首字母排序
// a-z 97-122 A-Z 65-90
String[] musics = new String[] {
"more and more","beautiful","Abstrat","welcome"};
for(int i = 0; i < musics.length - 1; i++) {
for(int j = 0; j < musics.length - 1 - i; j++) {
// 比较首字母
// 先取出j的首字母
char ch_j = musics[j].charAt(0);
// 统一转化为小写字母
if(ch_j < 97) {
ch_j = (char) (ch_j +32);
}
// 先取出j+1的首字母
char ch_j1 = musics[j+1].charAt(0);
if(ch_j1 < 97) {
ch_j1 = (char) (ch_j1 +32);
}
if(ch_j > ch_j1) {
String temp;
temp = musics[j];
musics[j] = musics[j+1];
musics[j+1] = temp;
}
}
}
for(String string : musics) {
System.out.print(string + ", ");
}
3 快速排序
算法思想:基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法),然后分别从数组的两端扫描数组,设两个指示标志(lo指向起始位置,hi指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换lo和hi位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换lo和hi位置的值,如此往复循环,直到lo>=hi,然后把基准点的值放到hi这个位置。一次排序就完成了。以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。
代码如下:
public static int partition(int []array, int lo, int hi){
//固定的切分方式
int key = array[lo];
while(lo < hi){
while(array[hi] >= key && hi > lo){//从后半部分向前扫描
hi--;
}
array[lo]=array[hi];
while(array[lo]<=key&&hi>lo){ // 从前半部分向后扫描
lo++;
}
array[hi]=array[lo];
}
array[hi]=key;
return hi;
}
public static void sort(int[] array,int lo ,int hi){
if(lo >= hi){
return ;
}
int index = partition(array,lo,hi);
sort(array,lo,index-1);
sort(array,index+1,hi);
}
快速排序的优化:
对于基准位置的选取一般有三种方法:固定切分,随机切分和三取样切分。固定切分的效率并不是太好,随机切分是常用的一种切分,效率比较高,最坏情况下时间复杂度有可能为O(N2)。对于三数取中选择基准点是最理想的一种。
public static int partition(int []array, int lo, int hi) {
//三数取中
int mid = lo + (hi - lo) / 2;
if(array[mid] > array[hi]) {
swap(array[mid], array[hi]);
}
if(array[lo] > array[hi]){
swap(array[lo], array[hi]);
}
if(array[mid] > array[lo]){
swap(array[mid], array[lo]);
}
int key = array[lo];
while(lo < hi){
while(array[hi] >= key && hi > lo) {
hi--;
}
array[lo] = array[hi];
while(array[lo] <= key && hi > lo){
lo++;
}
array[hi] = array[lo];
}
array[hi] = key;
return hi;
}
public static void swap(int a, int b){
int temp=a;
a=b;
b=temp;
}
public static void sort(int[] array, int lo, int hi){
if(lo>=hi){
return ;
}
int index = partition(array, lo, hi);
sort(array,lo,index-1);
sort(array,index+1,hi);
}
快速排序在序列中元素很少时,效率将比较低,不比插入排序。因此一般在序列中元素很少时使用插入排序,这样可以提高整体效率。
4 二叉树排序
在计算机科学中,二叉树是每个结点最多有两个子树的树结构。二叉树由各种节点组成。
1.每个节点都可以有左子节点,右子节点
2.每一个节点都有一个值
二叉树的遍历:左序,中序,右序
左序即: 中间的数遍历后放在左边
中序即: 中间的数遍历后放在中间
右序即: 中间的数遍历后放在右边
插入数据,插入基本逻辑是,小、相同的放左边,大的放右边
package practice;
public class Tree {
public Integer data; // 节点值
public static Tree root; // 根节点(唯一个)
public Tree father; // 父节点
public Tree leftChild; // 左孩子
public Tree rightChild; // 右子节点
// 左树是否为空
public boolean hasLeftChild() {
return leftChild != null;
}
public boolean hasRightChild() {
return rightChild != null;
}
/**
* 插入节点
* 先让data和root的值比较,小于0插入左边,大于0插入右边
* 递归思想
*/
public void insert(Integer data, Tree father) {
if(data.compareTo(father.data) == 0) {
return;
}
if(data.compareTo(father.data) < 0) {
// 父节点没有左节点
if(!father.hasLeftChild()) {
father.leftChild = new Tree(); // 生成一个左节点
father.leftChild.data = data; // 给左结点赋值
father.leftChild.father = father; // 指定左结点的父亲
} else {
insert(data, father.leftChild);
}
}
if(data.compareTo(father.data) > 0) {
// 父节点没有左节点
if(!father.hasRightChild()) {
father.rightChild = new Tree();
father.rightChild.data = data;
father.rightChild.father = father;
} else {
insert(data, father.rightChild);
}
}
}
/**
* 判断是否有树根,没有的话将数据添加到数根里
* 有树根的话调用insert的重载方法,判断插入到左子树还是右子树
* @param data
*/
public void insert(Integer data) {
if(root == null) {
root = new Tree();
root.data = data;
return;
} else {
insert(data,root);
}
}
/**
* 遍历树中的集合
* @param args
*/
public void outputTree(Tree tree) {
System.out.print(tree.data + " ");
if(tree.hasLeftChild()) {
outputTree(tree.leftChild);
}
if(tree.hasRightChild()) {
outputTree(tree.rightChild);
}
}
/**
* 找出树中最小值
* 找出最大值getmaxValue(tree.rightChild);
* @param args
*/
public void getminValue(Tree tree) {
if(tree.hasLeftChild()){
getminValue(tree.leftChild);
}else{
System.out.println("最小值"+tree.data);
}
}
public static void main(String[] args) {
Tree tree = new Tree();
tree.insert(5);
tree.insert(2);
tree.insert(3);
tree.insert(1);
tree.insert(8);
tree.insert(3);
tree.outputTree(tree.root);
System.out.println();
tree.getminValue(tree.root);
}
}
5 插入排序
插入排序算法:
- 1、以数组的某一位作为分隔位,比如index=1,假设左面的都是有序的.
- 2、将index位的数据拿出来,放到临时变量里,这时index位置就空出来了.
- 3、从leftindex=index-1开始将左面的数据与当前index位的数据(即temp)进行比较,如果array[leftindex]>temp,则将array[leftindex]后移一位,即array[leftindex+1]=array[leftindex],此时leftindex就空出来了.
- 4、再用index-2(即leftindex=leftindex-1)位的数据和temp比,重复步骤3,
- 直到找到<=temp的数据或者比到了最左面(说明temp最小),停止比较,将temp放在当前空的位置上.
- 5、index向后挪1,即index=index+1,temp=array[index],重复步骤2-4,直到index=array.length,排序结束,
- 此时数组中的数据即为从小到大的顺序.
public class Insert {
public int[] array;
public Insert(int[] array) {
this.array = array;
}
public void insertArray() {
int i, j, temp;
for(i = 1; i < array.length; i++) {
if(array[i]<array[i-1]){
temp = array[i];
for(j = i - 1; j >= 0 && array[j] > temp; j--) {
array[j+1] = array[j];
}
array[j+1] = temp;
}
}
System.out.println(Arrays.toString(array));
}
public static void main(String[] args) {
int[] array = {23,34,15,20};
Insert insert = new Insert(array);
insert.insertArray();
}
}
6 堆排序
堆是一种特殊的树形数据结构,其每个节点都有一个值,通常提到的堆都是指一颗完全二叉树,根结点的值小于(或大于)两个子节点的值,同时,根节点的两个子树也分别是一个堆。
堆排序就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的 n-1 个序列重新构造成一个堆,这样就会得到 n 个元素中次大的值。如此反复执行,便能得到一个有序序列了。
堆排序的实现需要解决的两个关键问题:
(1)将一个无序序列构成一个堆。
(2)输出堆顶元素后,调整剩余元素成为一个新堆。
package practice;
import java.util.Arrays;
public class Heap {
/**
* 构建大顶堆
*/
public static void isHeap(int[] a, int i, int len) {
int temp, j;
temp = a[i];
for(j = 2 * i; j < len; j *= 2) { // 沿关键字较大的孩子结点向下筛选
if(j < len && a[j] < a[j + 1])
++j; // j为关键字中较大记录的下标
if(temp >= a[j])
break;
a[i] = a[j];
i = j;
}
a[i] = temp;
}
public static void heapSort(int[] a) {
int i;
for(i = a.length / 2 - 1; i >= 0; i--) {// 构建一个大顶堆
isHeap(a, i, a.length - 1);
}
for(i = a.length - 1; i >= 0; i--) {// 将堆顶记录和当前未经排序子序列的最后一个记录交换
int temp = a[0];
a[0] = a[i];
a[i] = temp;
isHeap(a, 0, i - 1);// 将a中前i-1个记录重新调整为大顶堆
}
}
public static void main(String[] args) {
int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
heapSort(a);
System.out.println(Arrays.toString(a));
}
}