基本算法
一 排序算法
1. 冒泡排序
①将序列中所有元素两两比较,将最大的放在最后面。
②将剩余序列中所有元素两两比较,将最大的放在最后面。
③重复第二步,直到只剩下一个数。
public void bubbleSort(int[] array){
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j+1]){
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
2. 选择排序
①遍历整个序列,将最小的数放在最前面。
②遍历剩下的序列,将最小的数放在最前面。
③重复第二步,直到只剩下一个数。
public void selectSort(int[] array){
for (int i = 0; i < array.length; i++) {
int position=i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < array[position]) {
position = j;
}
if (position != i) {
int temp = array[i];
array[i] = array[position];
array[position] = temp;
}
}
}
}
3. 快速排序
选择第一个数为p,小于p的数放在左边,大于p的数放在右边。递归的将p左边和右边的数都按照第一步进行,直到不能递归。
public static int getMiddle(int[] array, int low, int high){
int mid = array[low];//将数组第一个 数作为中轴
while (low < high) {
while (low < high && array[high] > mid) {
high--;
}
array[low] = array[high];//比中轴小的记录移到低端
while (low < high && array[low] < mid) {
low++;
}
array[high] = array[low];//比中轴大的记录移到高端
}
array[low] = mid;//中轴记录到尾
return low;// 返回中轴的位置
}
public static void quickSort(int[] array, int low, int high){
if (low < high) {
int mid = getMiddle(array, low, high);//将numbers数组进行一分为二
quickSort(array, low, mid - 1);//对低字段表进行递归排序
quickSort(array, mid + 1, high);//对高字段表进行递归排序
}
}
4. 归并排序
速度仅次于快速排序,内存少的时候使用,可以进行并行计算的时候使用。选择相邻两个数组成一个有序序列。选择相邻的两个有序序列组成一个有序序列。重复第二步,直到全部组成一个有序序列。
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
public static void sort(int[] array, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
sort(array, low, mid);
// 右边
sort(array, mid + 1, high);
// 左右归并
merge(array, low, mid, high);
}
}
/**
* 将数组中low到high位置的数进行排序
* @param array 待排序数组
* @param low 待排的开始位置
* @param mid 待排中间位置
* @param high 待排结束位置
*/
public static void merge(int[] array, int low,int mid, int high){
int[] temp = new int[high - low + 1];
int left = low;
int right = mid + 1;
int k = 0;
// 把较小的数先移到新数组中
while (left <=mid && right<=high) {
if (array[left] < array[right]) {
temp[k++] = array[left++];
}else{
temp[k++] = array[right++];
}
}
// 把左边剩余的数移入数组
while(left <= mid){
temp[k++] = array[left++];
}
// 把右边剩余的数移入数组
while(right <= high){
temp[k++] = array[right++];
}
// 把新数组中的数覆盖array数组
for (int i = 0; i < temp.length; i++) {
array[i+low] = temp[i];
}
}
5. 希尔排序
希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
- 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
public static void shellSort(int[] array){
int len = array.length;
int h = 0;
while (h <= len) // 生成初始增量
{
h = 3 * h + 1;
}
while (h >= 1)
{
for (int i = h; i < len; i++)
{
int j = i - h;
int get = array[i];
while (j >= 0 && array[j] > get)
{
array[j + h] = array[j];
j = j - h;
}
array[j + h] = get;
}
h = (h - 1) / 3; // 递减增量
}
}
6. 堆排序
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的定义下:具有n个元素的序列 (h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
public class HeapSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array = Rand.getRandIntArray();
Rand.printArray(array);
int len = array.length;
for (int i = 0; i < len-1; i++) {
buildHeap(array, len-1-i);
swap(array, 0, len-1-i);
System.out.println(Arrays.toString(array));
}
Rand.printArray(array);
}
//建堆
public static void buildHeap(int[] array, int lastIndex){
//从lastIndex处节点(最后一个节点)的父节点开始
for (int i = (lastIndex-1)/2; i >= 0; i--) {
int k = i;
//如果当前k节点的子节点存在
while (k*2+1 <= lastIndex) {
int biggerIndex = k*2+1;
//如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
if (biggerIndex < lastIndex) {
if (array[biggerIndex] < array[biggerIndex + 1]) {
biggerIndex++;
}
}
if (array[k] < array[biggerIndex]) {
swap(array, k, biggerIndex);
//将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
k = biggerIndex;
}else{
break;
}
}
}
}
//交换堆顶与堆的最后一个位置
private static void swap(int[] data, int i, int j) {
int tmp=data[i];
data[i]=data[j];
data[j]=tmp;
}
}
7. 基数排序
用于大量数,很长的数进行排序时。
将所有的数的个位数取出,按照个位数进行排序,构成一个序列。
将新构成的所有的数的十位数取出,按照十位数进行排序,构成一个序列。
public static void baseSort(int[] a) {
//首先确定排序的趟数;
int max = a[0];
for (int i = 1; i < a.length; i++) {
if (a[i] > max) {
max = a[i];
}
}
int time = 0;
//判断位数;
while (max > 0) {
max /= 10;
time++;
}
//建立10个队列;
List<ArrayList<Integer>> queue = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < 10; i++) {
ArrayList<Integer> queue1 = new ArrayList<Integer>();
queue.add(queue1);
}
//进行time次分配和收集;
for (int i = 0; i < time; i++) {
//分配数组元素;
for (int j = 0; j < a.length; j++) {
//得到数字的第time+1位数;
int x = a[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
ArrayList<Integer> queue2 = queue.get(x);
queue2.add(a[j]);
queue.set(x, queue2);
}
int count = 0;//元素计数器;
//收集队列元素;
for (int k = 0; k < 10; k++) {
while (queue.get(k).size() > 0) {
ArrayList<Integer> queue3 = queue.get(k);
a[count] = queue3.get(0);
queue3.remove(0);
count++;
}
}
}
}
8. 直接插入排序
直接插入排序(Straight Insertion Sorting)的基本思想:在要排序的一组数中,假设前面(n-1) [n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
public static void insertSort(int [] a){
int len=a.length;//单独把数组长度拿出来,提高效率
int insertNum;//要插入的数
for(int i=1;i<len;i++){//因为第一次不用,所以从1开始
insertNum=a[i];
int j=i-1;//序列元素个数
while(j>=0 && a[j]>insertNum){//从后往前循环,将大于insertNum的数向后移动
a[j+1]=a[j];//元素向后移动
j--;
}
a[j+1]=insertNum;//找到位置,插入当前元素
}
}
二 查找算法
1. 折半查找
前提条件:已排序的数组中查找
二分查找的基本思想是:首先确定该查找区间的中间点位置: int mid = (low+upper) / 2;然后将待查找的值与中间点位置的值比较:若相等,则查找成功并返回此位置。若中间点位置值大于待查值,则新的查找区间是中间点位置的左边区域。若中间点位置值小于待查值,则新的查找区间是中间点位置的右边区域。下一次查找是针对新的查找区间进行的。
public static int binarySearch(int[] array, int number){
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high)/2;
if (array[mid] > number) {
high = mid - 1;
}else if (array[mid] < number) {
low = mid + 1;
}else{
return mid;
}
}
return -1;
}
三 全排列
简单地说:就是第一个数分别以后面的数进行交换
E.g:E = (a , b , c),则 prem(E)= a.perm(b,c)+ b.perm(a,c)+ c.perm(a,b)
然后a.perm(b,c)= ab.perm(c)+ ac.perm(b)= abc + acb.依次递归进行
public class PermutationSequence {
protected static int total = 0;
public static void main(String[] args) {
// TODO Auto-generated method stub
String str[] = {"a","b","c"};
perm(str, 0, str.length);
System.out.println(total);
}
public static void perm(String[] array, int cur, int count){
if (cur == count - 1) {
for (int i = 0; i < count; i++) {
System.out.print(array[i]+ " ");
}
System.out.println();
total++;
}else{
for (int j = cur; j < count; j++) {
swap(array, cur, j);
perm(array, cur + 1, count);
swap(array, cur, j);
}
}
}
public static void swap(String[] str, int a, int b){
String temp = new String();
temp = str[a];
str[a] = str[b];
str[b] = temp;
}
}