方法一:利用Arrays封装好的函数
int [] a={2,1,6,5};
Arrays.sort(a);
方法二:冒泡排序--时间复杂度O(n^2)
冒泡排序就是重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
public int[] sort(int[] a) {
int temp;
for(int i=0;i<a.length;i++){
for(int j=i+1;j<a.length;j++){
if(a[i]>a[j]){
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
return a;
}
方法三:选择排序--时间复杂度O(n^2)
先找到最小元素所在位置的索引,然后将该元素与第一位上的元素进行交换。
public int[] sort(int[] a) {
int temp=0;
for(int i=0;i<a.length;i++){
int minindex=i;
for(int j=i+1;j<a.length;j++){
minindex=a[j]<a[minindex]?j:minindex;
}
//swap(a,i,minindex);
temp=a[i];
a[i]=a[minindex];
a[minindex]=temp;
}
return a;
}
利用位运算,注意i!=minindex
public int[] sort(int[] a) {
for(int i=0;i<a.length;i++){
int minindex=i;
for(int j=i+1;j<a.length;j++){
minindex=a[j]<a[minindex]?j:minindex;
}
if(i!=minindex){swap(a,i,minindex);}
}
return a;
}
public void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
方法四:插入排序(时间复杂度最差O(n^2),最好O(n))
对于i从0到length,每一轮循环做到,0~i之间有序
public int[] sort(int[] a){
for(int i=0;i<a.length;i++){
for(int j=i;j>0;j--){
if(a[j-1]>a[j]){
swap(a,j-1,j);
}
}
}
return a;
}
public void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
方法五:归并排序(时间复杂度O(nlogn))
左侧有序,右侧有序,最后归并
class Solution {
/*归并排序
* 左右分别有序后,通过新开辟的数组将其整合
* */
public int[] sort(int [] arr){
process(arr,0, arr.length-1);
return arr;
}
public void process(int[] arr,int left,int right){//递归实现
if(left==right){
return ;
}
int mid=left+(right-left)/2;//防止溢出
process(arr,left,mid);
process(arr,mid+1,right);//将left到right之间已经分成两半有序整合
merge(arr,left,mid,right);
}
public void merge(int []arr,int left,int mid,int right){
//将left到right之间已经分成两半有序整合
int[] help=new int[right-left+1];
int i=0;//help数组里的index
int p1=left;//指引前一半有序数组中的指针,left~mid
int p2=mid+1;//指引后一半有序数组中的指针,mid+1~right
while(p1<=mid&&p2<=right){
help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
//i++先返回值再自增;---->++i,先自增再++
//这里比较前一半和后一半有序的数中,哪个数小,便放置于help数组里,但是当p1,或者p2已经超出范围时便停止
}
while(p1<=mid){
help[i++]=arr[p1++];
}
while(p2<=right){
help[i++]=arr[p2++];
}
for(int k=0;k<help.length;k++){
arr[left+k]=help[k];//从left到right之中要有序,len=right-left-1
}
}
}
归并排序拓展:小和问题,逆序对
方法六:快速排序O(n^2)
pivot的划分影响快速排序的时间复杂度
快速排序
单边循环--选择最右边的数为pivot,然后slow维护比pivot小的值的边界,然后fast从左向右去扫描,扫到一个比pivot小的数就将slow与fast所指向的值进行交换,然后边界值需要++
class Solution {
public int[] pivotSort(int[] arr){
//快排
singleprocess(arr,0,arr.length-1);
// doubleprocess(arr,0,arr.length-1);
return arr;
}
public void singleprocess(int[] arr,int left,int right){
if(left>=right){
return;
}
//单边快排
if(left>=right){
return;
}
int pivot=arr[right];
int slow=left,fast=left;
while(fast<right){
if(arr[fast]<pivot){
swap(arr,slow,fast);
slow++;//slow维护小于pivot的边界
}
fast++;
}
// System.out.println("fast:"+fast+" slow:"+slow);
swap(arr,fast,slow);
singleprocess(arr,left,slow-1);
singleprocess(arr,slow+1,right);
}
public void swap(int[]arr,int a,int b){
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
}
双边循环
选择一个pivot数,左边为<其的区域,右边为>其的区域,中间为相等区域
可以在两侧设置快慢指针,右边为快指针,每一次都是快指针先移动
class Solution {
/*快速排序
* 选择一个pivot数,左边为<其的区域,右边为>其的区域,中间为相等区域
* 可以在两侧设置快慢指针,右边为快指针,每一次都是快指针先移动
* */
public int[] sort(int [] arr){
process(arr,0, arr.length-1);
return arr;
}
public void process(int[] arr,int left,int right){//递归实现
if(left>right){
return;
}
int pivot=arr[left];//设置pivot
int slow=left,fast=right;
while(slow!=fast){
while (arr[fast]>=pivot&&slow<fast){
// 当右侧一直比pivot大时,fast指针自减
fast--;
}//右侧本来应该是>pivot,那么在右侧发现的比pivot小的数先暂停-->退出while循环,等待慢指针
while(arr[slow]<=pivot&&slow<fast){
slow++;
}
swap(arr,fast,slow);
}
arr[left]=arr[fast];
arr[slow]=pivot;//退出循环的条件是slow=fast这边用slow或者fast都可以
//这边结束是一轮排序--->开始递归--->slow前后的值已经达到我们初始的标准,则在这两个区域再次进行
process(arr,left,slow-1);//注意范围
process(arr,fast+1,right);
}
public void swap(int [] arr,int a,int b){//arr数组中a,b两处的值做交换
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
}
方法七:堆排序
将arr数组中的index序列数看作一个完全二叉树结构,可得
父节点索引:(i-1)/2
左孩子(left)结点:2*i+1
右孩子(right)结点:left+1
大根堆:arr(i)>arr(2i+1) && arr(i)>arr(2i+2)
小根堆:arr(i)<arr(2i+1) && arr(i)<arr(2i+2)
class Solution {
/*堆排序
*1.对于数组中的值构造初始大根堆-->大的数往上翻
*2.取出大根堆最上面的值移到末尾(leafsize--)即人为的把已经有序的数踢出去
*3.将剩下的数构造成大根堆-->当超过leafsize的数自动忽略,比较左孩子和右孩子之间的最大值是否比他们的父节点大,大的话就翻上去
*4.直到leafsize=1停止。
* */
public int[] sort(int [] arr){
firstLeaf(arr);
int leafsize= arr.length;
// swap(arr,0, leafsize--);//操作2,初始情况有序,把最大的数移到后面去
while (leafsize>1){
swap(arr,0,leafsize-1);
leafsize--;
leafify(arr,leafsize,0);//继续构造大根堆,操作3
}
return arr;
}
public void firstLeaf(int[] arr){//操作1
for(int i=0;i<arr.length;i++){
//i为数组中放进堆里面的index值,即与其父节点去做比较,大的往上翻
int fatherindex=(i-1)/2;
int currentindex=i;//类似于temp,因为需要保留i值进行迭代
while(arr[fatherindex]<arr[currentindex]){
//如果他翻上去了一次之后,他本身还比他的父节点所指向的数大的话继续往上翻,所以用while,不止进行一次判断
swap(arr,fatherindex,currentindex);
currentindex=fatherindex;
fatherindex=(currentindex-1)/2;
}//退出该while循环的条件为已经完全翻上去了--->他上面没有比其大的数
}
}
public void leafify(int[] arr,int leafsize,int index){
//将最顶上的数与其孩子结点比较
int left=index*2+1;int right=left+1;
int largest=-1;
while(left<leafsize) {//看其左孩子是否越界
largest = (right < leafsize) && (arr[right] > arr[left]) ? right : left;//右节点存在的时候还大的话肯定是右节点大,不存在的话就是左节点了
largest = arr[index] > arr[largest] ? index : largest;//跟父亲结点做比较
//这两行把最大的那个索引数给了largest
if (largest == index) {
break;
}
//不是index的话就要交换
swap(arr,largest,index);
index=largest;
left=index*2+1;
}
}
public void swap(int [] arr,int a,int b){//arr数组中a,b两处的值做交换
arr[a]=arr[a]^arr[b];
arr[b]=arr[a]^arr[b];
arr[a]=arr[a]^arr[b];
}
}