动画结合更利于学习.
将最大(最小)的数字逐个的向外“冒”出,时间复杂度O(n2)。
内层循环: 相邻两个数字比较,满足条件则交换,此处的j
外层循环: 当需要冒泡的数字只剩下一个时停止
public static void bubbleSort(int[] arra){
for (int i = 0; i < arra.length-1; i++) {
//相邻两两交换将最大的数字往后冒泡
for (int j = 0; j < arra.length-i-1; j++) {
if(arra[j] > arra[j+1]){
int temp = arra[j];
arra[j] = arra[j+1];
arra[j+1] = temp;
}
}
}
}
时间复杂度O(n2)
外层遍历,若当前数字比前一个数字小,记录当前数字遍历此数字前的数字,直到比前一个数字大,然后将该数字插入到该数字的后面
public static void insertSort(int[] arra){
//遍历所有的数字
for (int i = 1; i < arra.length; i++) {
//如果当前数字比前一个数字小
if(arra[i] < arra[i-1]){
//存储当前数字
int temp = arra[i];
int j;
//遍历此数字之前的所有数字,直到该数字temp不小于前一个数字
for (j = i-1; j >= 0 && temp < arra[j]; j--) {
arra[j+1] = arra[j];
}
arra[j+1] = temp;
}
}
}
//有设定步长,解决较小的数字排在最后的情况
public static void shellSort(int[] arra){
//步长逐渐除以2
for (int step = arra.length/2; step>0; step/=2) {
//遍历从arra[step]开始,
for (int i = step; i < arra.length; i++) {
//与step步之前的元素相比较
for (int j = i - step; j >= 0; j -= step) {
if(arra[j] > arra[j+step]){
int temp = arra[j];
arra[j] = arra[j+step];
arra[j+step] = temp;
}
}
System.out.println(Arrays.toString(arra));
}
}
}
时间复杂度O(nlgn),不容易理解
利用分治思想,当时严老师C语言版数据结构中学过顺序表的合并,merge方法中的基本语句也就是体现了这样一种思想,学习这一算法的时候可以由简到繁。
例如:
public static void main(String[] args) {
int[] arra = new int[]{1,3,5,2,4,6};
int[] temp = new int[arra.length];
//
int low = 0, high = arra.length - 1;
//中间位置
int mid = (low + high) / 2;
//双指针
int i = 0, j = mid + 1;
System.out.println(i+" "+j);
//临时下标
int index = 0;
//归并
while(i <= mid && j <= high){
//合并顺序数组
if(arra[i] < arra[j]){
temp[index++] = arra[i++];
}else{
temp[index++] = arra[j++];
}
}
//前半个数组有剩余
while(i <= mid){
temp[index++] = arra[i++];
}
//后半个数组有剩余
while(j <= high){
temp[index++] = arra[j++];
}
System.out.println(Arrays.toString(temp));
}
基本语句了解后来了解分治,若给定无序数组{1,5,4,8,6,2,10}又将如何排序
//分治思想:一部分递归实例
1 5 4 8 6 2 10
| | |
此时low = 0, high = 6, mid = 3
--------开始分(向左递归,直到剩下一个数字,他本身就有序,自己可以拿纸画)----
{1,5,4,8} {6,2,10}
{1,5,4} {8}
{1,5} {4}
{1}
我现在的理解就是分开治理,把一大部分分成几小部分,逐渐整体有序
//分
public static void mergeSort(int[] arra, int low, int high){
if(low<high){
int mid = (low+high)/2;
//处理左边
mergeSort(arra, low, mid);
//处理右边
mergeSort(arra, mid+1, high);
//归并
merge(arra, low, mid, high);
}
}
public static void merge(int[] arra, int low, int mid, int high){
//临时数组存储分治好的有序元素
int[] temp = new int[high-low+1];
//记录第一个数组需要遍历的下标
int i = low;
//记录第二个数组需要遍历的下标
int j = mid+1;
//记录在临时数组中存放的下标
int index = 0;
//遍历两个数组取出最小的数字,放入临时数组中,有序数组的合并
while(i<=mid && j<=high){
if(arra[i]<arra[j]){
temp[index] = arra[i];
i++; index++;
}else{
temp[index] = arra[j];
j++; index++;
}
}
while(i<=mid){
temp[index] = arra[i];
i++; index++;
}
while(j<=high){
temp[index] = arra[j];
j++; index++;
}
//将排好序的temp存入arra
for(int k=0;k<temp.length;k++){
arra[low+k] = temp[k];
}
}
public static void selectSort(int[] arra){
//遍历所有的数
for (int i = 0; i < arra.length-1; i++) {
int index = i;
//把当前遍历的元素和后面的所有元素依次比较
for (int j = i + 1; j < arra.length; j++) {
//记录下最小的数字下标
if(arra[j] < arra[i]){
index = j;
}
}
//内层循环完成后,如果此时的最小数字min发生变化,就将此时的min替换arra[i]
if(index != i){
int temp = arra[index];
arra[index] = arra[i];
arra[i] = temp;
}
}
}
双指针加分治,时间复杂度O(nlgn)
public static void quickSort(int[] arra, int left, int right){
if(left<right){
int i = left, j = right;
//一趟排序
//选定基准位
int temp = arra[left];
//当i>j时结束循环
while(i < j){
//j-- 直到arra[j]的值小于temp时停止,将arra[i] = arra[j]
while(i<j && temp<=arra[j]) j--;
arra[i] = arra[j];
//i++ 直到arra[i]的值大于temp的时候停止,arra[j] = arra[i]
while(i<j && temp>=arra[i]) i++;
arra[j] = arra[i];
}
//arra[i] = temp
arra[i] = temp;
//多趟分治
quickSort(arra, left, i-1);
quickSort(arra, i+1, right);
}
}
算法思想容易理解,代码较长
最外层循环次数,为数组中元素的最大位数
创建编号0到9十个桶,分别取数字的个位,十位,百位。。。入桶,再出桶
public static void radixSort(int[] arra){
//存最大的数字
int max = Integer.MIN_VALUE;
for(int i=0;i<arra.length;i++){
if(arra[i]>max){
max = arra[i];
}
}
//计算最大的数字位数
int maxLength = (max+"").length();
//用于临时村塾数据的数组
int[][] temp = new int[10][arra.length];
//标记数组,记录temp每列数组的长度
int[] counts = new int[10];
//根据最大长度的数决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10){
for(int j=0;j<arra.length;j++){
//第i次放入桶中
int ys = arra[j]/n%10;
temp[ys][counts[ys]] = arra[j];
counts[ys]++;
}
//出桶
int index = 0;
for(int k=0;k<counts.length;k++){
//k为temp的行数,counts[k]为temp在该行的数据元素长度
if(counts[k]!=0){//表示该桶内有数据
for(int m=0;m<counts[k];m++){
arra[index++] = temp[k][m];
}
//将counts[k]置为0。将桶清空,以便于进行下一步的计数
counts[k] = 0;
}
}
}
}
/**
* 堆排序,将大根堆根节点的数字与数组末尾的数字交换位置,
* 堆中元素长度-1
*
* @param arra
*/
public static void heapSort(int[] arra){
//开始位置是最后一个非叶子结点,即最后一个结点的父结点
int start = (arra.length-1)/2;
//建立大根堆
for(int i=start;i>=0;i--){
maxHeap(arra, arra.length, i);
}
//开始排序
for(int i=arra.length-1;i>=0;i--){
//将大根堆根节点的数字与数组末尾的数字交换位置,最大的元素放在数组末尾
int temp = arra[i];
arra[i] = arra[0];
arra[0] = temp;
//此时只有堆顶的元素(下标为0)不满足大根堆,需要调整
maxHeap(arra, i, 0);
//再次满足大根堆,堆中元素长度-1
}
}
/**
* 建立大根堆
* [10, 9, 6, 5, 4, 1, 2]
* 父结点的权大于子节点的权
*/
public static void maxHeap(int[] arra, int size, int index){
//存储当前结点,假设当前结点的值最大
int max = index;
//左子节点下标
int leftNode = 2*index+1;
//右子节点下标
int rightNode = 2*index+2;
//与两个子结点数值比较,找出数值最大的结点下标
if(leftNode<size && arra[leftNode]>arra[max]){
max = leftNode;
}
if(rightNode<size && arra[rightNode]>arra[max]){
max = rightNode;
}
//如果max的值发生变化
if(max != index){
//交换索引下标对应的数字
int temp = arra[index];
arra[index] = arra[max];
arra[max] = temp;
//System.out.println(Arrays.toString(arra));
//交换完成最大的数字为两个较小子节点的父节点,
//交换后需要避免对arra[max]在原处时的子节点产生影响,需要递归处理
maxHeap(arra, size, max);
}
}