最近在面试的时候,面试官都会问到一些算法题,这里我把一些课本上有的(算法导论)算法的java实现版本整理一下,方便自己查阅,如果里面存在错误的话,请及时指正,谢谢。
快排的思想是分支,具体介绍请查看别的博客,这里我主要说明两点内容:
1)为什么最后需要把你选择的某个anchor点交换,比如说,我在程序里面使用的输入数组的第一个元素,为什么需要把第一个元素和end指针(end指针用于从后往前找到小于anchor的点)进行交换?因为如果你不进行交换的话,对于一些偏序的情况,如倒序的情况,是不可行的。而且,每次交换元素之后保证了我们每次至少有一个元素是已经排好序的。
2)对于选择第一个元素作为anchor点时,必须首先从后开始往前找小于anchor的点,因为这样保证的end指针找的节点是小于anchor的,可以用于交换。如果反过来,首先从前找到大于anchor的点,那么end指向的点可能大于anchor,就不能用于交换(end到达start处停止循环)
package Algorithm;
public class QuickSort {
public static void main(String[] args) {
int arr[] = {10, 7, 8, 9, 1, 5};
//int arr[] = {10, 10, 10, 10, 10};
int n = arr.length;
QuickSort ob = new QuickSort();
ob.sort(arr, 0, n-1);
System.out.println("sorted array");
printArray(arr);
}
int partition(int arr[], int low, int high)
{
int pivot = arr[high];
int i = (low-1); // index of smaller element
for (int j=low; jpivot && start
参考:
https://www.geeksforgeeks.org/quick-sort/
快排对于一些极端的数据来说,可能会具有较低的性能,比如说顺序,倒序,很多数相等,等等。可以通过一些手段来提升这些情况的快排性能。
1)对于顺序,逆序来说,可以先对数据进行shuffle,打乱顺序之后在进行快排,下面仅仅写出shuffle函数:
//O(n)
private static void shuffleArray(int[] ar) {
Random rnd = new Random();
for (int i = ar.length - 1; i > 0; i--) {
int index = rnd.nextInt(i + 1); // random between 0 and i
exchange(ar, i, index);
}
}
public static void exchange(int[] input, int i, int j) {
int temp = input[i];
input[i] = input[j];
input[j] = temp;
}
2)对于很多数据相等,比如说全是0,快排的时间复杂度非常高,相当于每次产生一个长度为n-1和1的子序列,因此时间复杂度是O( ),因此,我们可以把每次和pivot相等的数据找出来,处理剩下的不相等的子序列,那么效率就很高了。该方法叫three-way-quicksort,详情请查看https://dzone.com/articles/algorithm-week-quicksort-three,下面是代码:
package Algorithm;
import java.util.Random;
public class ThreeWayQuickSort {
public static void main(String[] args) {
int dataSize = 1000000;
int oneRate = 100;
int[] arr = generateData(dataSize, oneRate);
int n = arr.length;
ThreeWayQuickSort ob = new ThreeWayQuickSort();
ob.sort(arr, 0, n-1);
System.out.println("sorted array:");
printArray(arr);
}
public void sort(int[] input, int lowIndex, int highIndex) {
if (highIndex<=lowIndex) return;
//lt - gt is same num as pivot
//lt is the left most index of pivot
int lt=lowIndex;
int gt=highIndex;
int i=lowIndex+1;
int pivotIndex=lowIndex;
int pivotValue=input[pivotIndex];
while (i<=gt){
if (input[i]
堆排序的详细细节请查看别的博客,这里我主要说一点,就是建堆的时间复杂度是O(n),虽然建堆过程需要对非叶子节点进行堆维护过程,看起来像是O(NlogN),但是,因为不是所有节点都需要维护堆,而且需要维护的节点树高比较低,因此实际的时间复杂度上界是O(n),证明如下(拍自算法导论):
package Algorithm;
public class HeapSort {
public static void main(String[] args) {
int arr[] = {12, 11, 13, 5, 6, 7};
HeapSort ob = new HeapSort();
ob.sort(arr);
System.out.println("Sorted array is");
printArray(arr);
}
public void sort(int[] arr) {
int length = arr.length;
for(int i=length/2-1;i>=0;i--) {
heapify(arr, length, i);
}
for (int i=length-1; i>=0; i--)
{
// Move current root to end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
void heapify(int arr[], int n, int i) {
int largest = i; // Initialize largest as root
int l = 2*i + 1; // left = 2*i + 1
int r = 2*i + 2; // right = 2*i + 2
// If left child is larger than root
if (l < n && arr[l] > arr[largest])
largest = l;
// If right child is larger than largest so far
if (r < n && arr[r] > arr[largest])
largest = r;
// If largest is not root
if (largest != i)
{
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
static void printArray(int arr[])
{
int n = arr.length;
for (int i=0; i
参考:
https://www.geeksforgeeks.org/heap-sort/
使用迭代的方法,产生数据的组合以及排列。下面的代码中,permute代表产生排列,combination代表组合,排列的代码参考自"剑指offer",组合的代码是自己写的,如果有问题请指正。
package Algorithm;
import java.util.ArrayList;
import java.util.Collection;
public class Permutation {
public static void main(String[] args) {
int[] arr = new int[] {1,2,3};
ArrayList candidate = new ArrayList<>();
for(int i =0;i> result = new ArrayList<>();
ArrayList now = new ArrayList<>();
permute(result, candidate, now);
System.out.println("total num:"+result.size());
for(int i=0;i> result2 = new ArrayList<>();
ArrayList now2 = new ArrayList<>();
combination(result2, candidate, now2);
System.out.println("total num:"+result2.size());
for(int i=0;i> result, ArrayList candidate, ArrayList now) {
if(candidate.size()==1) {
now.add(candidate.get(0));
result.add(new ArrayList<>(now));
now.remove(now.size()-1);
} else {
for(int i=0;i temp = new ArrayList<>();
for(int j=0;j> result2, ArrayList candidate, ArrayList now) {
for(int i=1;i<=candidate.size();i++) {
combinationMain(result2, candidate, now, 0, i);
}
}
private static void combinationMain(ArrayList> result2, ArrayList candidate, ArrayList now, int start, int length) {
if(now.size()==length) {
ArrayList tempResult = new ArrayList<>(now);
result2.add(tempResult);
} else if(start
最邻近点对算法使用的是分治的策略,详情请查看https://www.geeksforgeeks.org/closest-pair-of-points-using-divide-and-conquer-algorithm/,下面是我的java版本,比较重要的一点就是,该算法的时间复杂度是O(nlgn)如下:
其中需要说明的一点就是merge阶段,merge的时候需要从中间的区域找到最近的点对,看似需要n^2的时间复杂度,但是实际上,每个点周围需要比较的点实际上最多只有6个,因此时间复杂度实际上是O(n).
package Algorithm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
/*复杂度为O(nlogn)*/
public class ClosestPair {
//比较器
public static Comparator compareX = new Comparator() {
@Override
public int compare(node o1, node o2) {
return o1.compareX(o1, o2);
}
};
public static Comparator compareY = new Comparator() {
@Override
public int compare(node o1, node o2) {
return o1.compareY(o1, o2);
}
};
public static void main(String[] args) {
node n1 = new node(2, 3);
node n2 = new node(12, 30);
node n3 = new node(40, 50);
node n4 = new node(5, 1);
node n5 = new node(12, 10);
node n6 = new node(3, 4);
node[] nodes = new node[] {n1, n2, n3, n4, n5, n6};
double min = closest(nodes);
System.out.println("closest distance is :"+min);
}
/*euclidean distance*/
public static double dist(node o1, node o2) {
return Math.sqrt(Math.pow(o1.x-o2.x, 2)+Math.pow(o1.y-o2.y, 2));
}
public static double closest(node[] nodes) {
Arrays.sort(nodes, compareX);
int end = nodes.length-1;
int start = 0;
return closestMain(nodes, start, end);
}
public static double closestStrip(ArrayList strip, double min) {
double result = min;
Collections.sort(strip, compareY);//这一步的意义在于保证每个点周围只有最多6个点用于比较
int size = strip.size();
for(int i=0;idr?dl:dr;
ArrayList strip = new ArrayList<>();
node midNode = nodes[mid];
for(int i=start;i<=end;i++) {
if(nodes[i].x-midNode.x-min<0.0000001) {
strip.add(nodes[i]);
}
}
return Math.min(min, closestStrip(strip, min));
}
}
}
class node{
public int x;
public int y;
public node(int x, int y) {
this.x = x;
this.y = y;
}
public int compareX(node node1, node node2) {
if(node1.x>=node1.x) {
return 1;
} else {
return 0;
}
}
public int compareY(node node1, node node2) {
if(node1.y>=node1.y) {
return 1;
} else {
return 0;
}
}
}