算法学习笔记
冒泡排序 时间复杂度O(n2)
public class BubbleSort {
public static void main(String[] args) {
int[] array={7,8,9,4,5,6,2,1,3};
sort(array);
for (int i = 0; i <array.length ; i++) {
System.out.print(array[i]+",");
}
}
public static void sort(int[] array){
for (int i = 0; i <array.length-1 ; i++) {
for (int j = 0; j <array.length-1-i ; j++) {
if (array[j]>array[j+1]){
int temp=array[j+1];
array[j+1]=array[j];
array[j]=temp;
}//if
} // forj
} //fori
} //sort
//改进 原理记录最后一位冒泡交换的下标,
public static void sort2(int[] array){
int m=array.length;
while (m > 1) {
int lastExchangeIndex = 1;
for (int j = 0; j < array.length-1; j++) {
if (array[j] > array[j + 1]) {
swap(array, j, j + 1);
lastExchangeIndex = j;
}else {
m = lastExchangeIndex; //只要最后一位交换的下标不是0,就一直循环
}
} // forj
} //while
} //sort
}
插入排序 时间复杂度 O(n2)
public class InsertSort {
public static void main(String[] args) {
int[] arr = {1,4,5,8,6,7,8,21,33};
insertSort(arr);
for (int ele:arr) {
System.out.println(ele);
}
}
//插入排序
public static void insertSort(int[] arr){
for (int j=1;j<arr.length;j++){
int key =arr[j];
int i=j-1;
while(i>-1&&arr[i]>key){
arr[i+1]=arr[i];
i=i-1;
}
arr[i+1]=key;
}
}
}
选择排序
public static void select_sort(int[] arr){
int n=arr.length;
for(int i=0;i<n;i++){
int minPosition=sacnForMin(arr,i,n-1);
swap(arr,arr[i],arr[minPosition]);
}
}
private static void swap(int[] arr, int i, int j) {
int temp=arr[j];
arr[j]=arr[i];
arr[i]=temp;
}
private static int sacnForMin(int[] arr, int i, int i1) {
int min=arr[i];
int position=i;
for (int j = i; j < i1; j++) {
if (arr[j]<min){
min=arr[j];
position=j;
}
}
return position;
}
归并排序 : 归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。java中Arrays.sort()采用了一种名为TimSort的排序算法,就是归并排序的优化版本。每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。
import java.util.Arrays;
public class MergeSort {
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr){
int []temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
mergeSort(arr,0,arr.length-1,temp);
}
private static void mergeSort(int[] arr,int left,int right,int []temp){
if(left<right){
int mid = (left+right)/2;
mergeSort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
mergeSort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
}
}
private static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
}
归并排序问题延伸
利用归并排序求一个数组中的逆序对
public class MergeSort {
private static int ORDER_COUNT=0;
public static void main(String []args) throws InterruptedException {
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
System.out.println(ORDER_COUNT);
int [] arr2={2,1,5,3,2};
Thread.sleep(1000);
ORDER_COUNT=0;
sort(arr2);
System.out.println(Arrays.toString(arr2));
System.out.println(ORDER_COUNT);
}
public static void sort(int []arr){
int []temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
mergeSort(arr,0,arr.length-1,temp);
}
private static void mergeSort(int[] arr,int left,int right,int []temp){
if(left<right){
int mid = (left+right)/2;
mergeSort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
mergeSort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
}
}
private static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
//求解归逆序对中的数量只需要改进一下while循环方法
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){ //此时没有逆序对
temp[t++] = arr[i++];
}else { //否则产生了逆序对,我们需要对逆序对做出统计
temp[t++] = arr[j++];
ORDER_COUNT+=mid-i+1;//+1的作用是将0的序列变为按下标的序列,
// mid+1是左边的总数,i是第一个比右边数值大的坐标
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
}
快速排序
//快速排序
// 分而治之 先选一个枢纽 左边元素全部小于枢纽 右边元素全部大于枢纽
//1.第一步选取主元 取头中尾的中位数 例如8,12,3的中位数就是8
//选取主元
public static void Median(int[] arr, int left, int right) {
int center = (left + right) / 2; // 一定是左中右的顺序 left<center<right
if (arr[left] > arr[center]) swap(arr, left, center);
if (arr[left] > arr[right]) swap(arr, left, right);
if (arr[center] > arr[right]) swap(arr, center, right);
swap(arr, right-1, center); //之后只需考虑arr[left+1]....arr[right-2]
}
//子集划分
public static void quickSoort(int[] arr, int left, int right) {
if (left < right) {
Median(arr, left, right);
int pivot = right - 1;
int i = left; //左指针
int j = right - 1; //右指针
while (true){
while (arr[++i] < arr[pivot]) { } //i=left+1
while (j > left && arr[--j] > arr[pivot]) { } // right-2
if (i < j) {
swap(arr, i, j);
} else {
break;
}
}
if (i < right) {
swap(arr, i, right - 1);
}
quickSoort(arr, left, i - 1);
quickSoort(arr, i + 1, right);
}//if
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
表排序v :待排元素不是一个简单的整数,待排元素是一个类,一个对象,你在排序的过程中不需要移动原始数据
物理排序: n个数字的排列由若干个独立的环组成
桶排序:
基数排序 : 次位优先
多关键字排序:
vivo的面试题
import java.util.Arrays;
/**
* 比较两个字符串大小规则如下:
* 1.如果两个字符串长度不相同,则短字符串比长字符串小
* 2.如果两个字符串长度相同,则按照A~ Z,a~z,0~9的大小关系排序
*/
public class StringSort {
public static void main(String[] args) {
String[] strs = {"a", "Abc", "123", "1", "abc", "CBD", "abcd", "a"};
sort(strs);
for (String str:strs) {
System.out.print(str+"\t");
}
}
/**
* 比较a和b的大小
* @param a
* @param b
* @return
*/
public static boolean less2(String a, String b){
if (a.length()<b.length()) return true;
else if (a.length()==b.length()){
for (int i = 0; i <a.length() ; i++) {
if (a.charAt(i)>='0'&&a.charAt(i)<='9') {
if (b.charAt(i) >= '0' && b.charAt(i) <= '9')
return b.charAt(i)>a.charAt(i)?true:false;
if (b.charAt(i)>='A'&&b.charAt(i)<='z') return false;
}else {
if (b.charAt(i) >= '0' && b.charAt(i) <= '9') return true;
else return b.charAt(i)>a.charAt(i)?true:false;
}
}
}
return false;
}
public static boolean less(String a,String b){
if (a.length()<b.length()) {
return true;
}else if (a.length()>b.length()) {
return false;
}else{
for (int i = 0;i<a.length();i++){
if(a.charAt(i)>=48&&a.charAt(i)<=57){
if(b.charAt(i)>=48&&b.charAt(i)<=57){
return a.charAt(i)<b.charAt(i);
}else{
return false;
}
}else{
if(b.charAt(i)>=48&&b.charAt(i)<=57){
return true;
}else{
return (int)a.charAt(i)<(int)b.charAt(i);
}
}
}
return true;
}
}
/**
* 以下为归并排序模板
* @param arr
*/
public static void sort(String []arr){
String []temp = new String[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
mergeSort(arr,0,arr.length-1,temp);
}
private static void mergeSort(String[] arr,int left,int right,String []temp){
if(left<right){
int mid = (left+right)/2;
mergeSort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
mergeSort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
}
}
private static void merge(String[] arr,int left,int mid,int right,String[] temp){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
while (i<=mid && j<=right){
if(less(arr[i],arr[j])){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
}
牛课练习 反转数组
题目描述
public class ListNode {
int val;
ListNode next = null;
public ListNode() {
}
ListNode(int val) {
this.val = val;
}
}
输入一个链表,反转链表后,输出新链表的表头。
//反转数组
public ListNode ReverseList(ListNode head){
if(head==null){
return null;
}
ListNode next=null;
listNode pre=null;
while(head!=null){
next=head.next;
head.next=pre;
pre=head;
head=next;
}
retuen pre;
}
衍生 局部链表反转
public class ReverseList {
public static void main(String[] args) {
ListNode l1=new ListNode(1);
ListNode l2=new ListNode(2);
ListNode l3=new ListNode(3);
ListNode l4=new ListNode(4);
ListNode l5=new ListNode(5);
ListNode l6=new ListNode(6);
ListNode l7=new ListNode(7);
ListNode l8=new ListNode(8);
l1.next=l2;
l2.next=l3;
l3.next=l4;
l4.next=l5;
l5.next=l6;
l6.next=l7;
l7.next=l8;
ListNode next=l1;
for (int i = 0; i <8 ; i++) {
System.out.print(next.val);
next=next.next;
}
System.out.println();
// ListNode newHead=reverseList(l1);
// for (int i = 0; i <8 ; i++) {
// System.out.println(newHead.val);
// newHead=newHead.next;
// }
reversePartOfList(l1,l1,l8);
ListNode frist=l8;
for (int i = 0; i <8 ; i++) {
System.out.print(frist.val);
frist=frist.next;
}
}
//下面开始测试局部链表翻转 123456789----123 987654 123456789 123 654 789
public static void reversePartOfList(ListNode node,ListNode begin,ListNode end){
ListNode p1=new ListNode(0);
p1.next=node;
ListNode p2=new ListNode(9);
ListNode temp0=p1;
while(temp0.next!=null){
temp0=temp0.next;
} temp0.next=p2; //将9 和 尾部最后一个节点相连
ListNode temp1=p1;
while (temp1.next!=begin){
temp1=temp1.next;
} p1=temp1;//哨兵1 记录断开的前一个节点
System.out.println(p1.val);
ListNode temp2=p1;
while (temp2.next!=null&&temp2.next!=end){
temp2=temp2.next;
}
p2=temp2.next.next;//哨兵 记录断开的后一个节点
System.out.println(p2.val);
end.next=null;
p1.next=null;
ListNode partBegin=reverseList(begin);
ListNode temp3=partBegin;
while (temp3.next!=null){
temp3=temp3.next;
}
ListNode partEnd=temp3;
System.out.println(partBegin.val+" "+partEnd.val);
p1.next=partBegin;
partEnd.next=p2;
}
//题目描述
//输入一个链表,反转链表后,局部翻转链表
public static ListNode reverseList(ListNode head) {
if (head==null){
return null;
}
ListNode pre=null;
ListNode next=null;
while(head!=null){
next=head.next;
head.next=pre;
pre=head;
head=next;
}
return pre;
}
}
代码较为冗余 经过多个测试代码 是正确的 如果有问题 还望指出。
头插法局部链表反转
逻辑 四个指针 head pre cur next head为哨兵不动 pre为尾节点值不变位置在移动 每次就是将cur插在head后面,将pre与next相连,举例 1 2 3 4 翻转23 就是 1=head 2=pre 3=cur 4=next 将3插在2前 完成头插
翻转234 就是在上一步基础上 将4插在 32前面 1后面 就完成了翻转
核心代码
//找到 head pre cur 三个节点 构造next节点
next=cur.next;
pre.next=head.next;
head.next=cur;
pre.next=next;
cur=next;
0 1 2 3 4 5 6 7 8
第i次0 next值3 cur值2 head值0 pre值1
第i次1 next值4 cur值3 head值0 pre值1
第i次2 next值5 cur值4 head值0 pre值1
第i次3 next值6 cur值5 head值0 pre值1
第i次4 next值7 cur值6 head值0 pre值1
第i次5 next值8 cur值7 head值0 pre值1
0 7 6 5 4 3 2 1 8
public class ReverseListByHead {
public static void main(String[] args) {
ListNode head=new ListNode();
ListNode l1=new ListNode(1);
ListNode l2=new ListNode(2);
ListNode l3=new ListNode(3);
ListNode l4=new ListNode(4);
ListNode l5=new ListNode(5);
ListNode l6=new ListNode(6);
ListNode l7=new ListNode(7);
ListNode l8=new ListNode(8);
head.next=l1;
l1.next=l2;
l2.next=l3;
l3.next=l4;
l4.next=l5;
l5.next=l6;
l6.next=l7;
l7.next=l8;
print(head);
reverseByHead(head,1,7);
print(head);
}
public static void print(ListNode head) {
while (head != null) {
System.out.print(head.val + " ");
head = head.next;
}
System.out.println();
}
/**
* 翻转链表的从结点m到结点n的部分
*
* @param head 头节点
* @param from 翻转的开始位置
* @param to 翻转的结束位置
* @return 翻转后的新链表
12345678 1 234567 8 1 765432 8
初始化的Head 头节点为空
*/
public static void reverseByHead(ListNode head, int from, int to) {
if(from>to)return;
ListNode cur=head.next;
int i;
for ( i = 0; i <from-1 ; i++) { //找到断开的前一个节点
head=cur; //将前一个节点 标记为 head
cur=cur.next; //
}
ListNode pre=cur; //反转的第一个节点即使翻转后的最后一个节点
cur=cur.next;
to--; //必须减减 否则会数组越界
ListNode next=null;
for (;i<to;i++){
next=cur.next;
cur.next=head.next; // 1234 5678 1=head 2= pre 3=cur 4=next 3.next=(1.next)2 3->2
head.next=cur; // 1->3->2
pre.next=next; //1->3->2->4 5678
System.out.println("第i次"+i+ " "+"next值"+next.val+" "+"cur值"+cur.val+" "+"head值"+head.val+" "+"pre值"+pre.val+" ");
cur=next; //推火车 13245678 1=head 3=head.next 2=pre 4=cur 5=next
}
}
}