JAVA算法

基本算法

算法总结
基本算法时空复杂度:

img

选择排序
import java.util.Arrays;

public class 选择排序 {
 public static void main(String[] args) {
     int[] arr={5,4,6,2,1,7,0,9,8,3};
     int[] res = sort(arr);
     System.out.println(Arrays.toString(res));
 }
 public static int[] sort(int[] data){
     int k=0;//选择指针
     int tmp=0;//用于交换位置的临时变量
     for (int i = 0; i < data.length - 1; i++) {
         //遍历第一个到倒数第二个元素
         k=i;//假设索引i的数是最小值
         for (int j = i+1; j < data.length; j++) {
             //遍历第二到最后一个元素
             if (data[j]<data[k]){
                 //如果索引j更小,说明k不是最小,要改
                 k=j;
             }
             //这个循环结束的时候k就是最小值的索引
         }
         //依次找出最小的值所在位置k,与从0开始遍历数组的元素交换
         if (k!=i){
             tmp=data[k];
             data[k]=data[i];
             data[i]=tmp;
         }
     }
     return data;
 }
}

冒泡排序
import org.junit.Test;

import java.util.Arrays;
//冒泡排序
public class 冒泡排序 {
 @Test
 public void a1(){
     int[] arr=new int[]{5,4,2,6,1,8,9,0,3,7};
     System.out.println(Arrays.toString(arr));
     //冒泡排序0-9
     int tmp = 0;
     for (int j = 0; j <arr.length ; j++) {
         for (int i=0;i<arr.length-j-1;i++){
             if (arr[i+1]<arr[i]){
                 tmp=arr[i];
                 arr[i]=arr[i+1];
                 arr[i+1]=tmp;
             }
         }
     }
     System.out.println(Arrays.toString(arr));
 }

 @Test
 public void a2(){
     int[] arr = new int[]{5,6,3,2,1,4,7,0,8,9};
     System.out.println(Arrays.toString(arr));
     int tmp=0;
     for (int i = 0; i < arr.length; i++) {
         //flag为真表示有序
         Boolean flag=true;
         for (int j = 0; j < arr.length-i-1; j++) {
             if (arr[j]>arr[j+1]){
                 tmp=arr[j];
                 arr[j]=arr[j+1];
                 arr[j+1]=tmp;
                 //if条件成立说明数组不是有序的
                 flag=false;
             }
         }
         if (flag){
             break;
         }
     }
     System.out.println(Arrays.toString(arr));
 }

 @Test
 public void a3(){
     int[] arr=new int[]{4,2,3,1,0,7,9,5,6};
     //最后交换顺序的位置
     int lastindex=0;
     //有序范围,首先假设全部有序
     int sortborder=arr.length-1;
     int tmp=0;
     for (int i = 0; i < arr.length; i++) {
         //有序标签.假设全部有序
         boolean flag=true;
         for (int j = 0; j < sortborder; j++) {
             if (arr[j]>arr[j+1]){
                 tmp=arr[j];
                 arr[j]=arr[j+1];
                 arr[j+1]=tmp;
                 //交换顺序
                 flag=false;
                 //改变最后交换顺序的位置
                 lastindex=j;
             }
         }
         //逐次缩小比较的范围
         sortborder=lastindex;
         if (flag){
             //没进入过交换顺序的话,说明已经有序,跳出循环
             break;
         }
     }
     System.out.println(Arrays.toString(arr));
 }

}

插入排序
public static int[] insertionSort(int[] array) {
 if (array.length == 0)
     return array;
 int current;
 for (int i = 0; i < array.length - 1; i++) {
     current = array[i + 1];
     int preIndex = i;
     while (preIndex >= 0 && current < array[preIndex]) {
         array[preIndex + 1] = array[preIndex];
         preIndex--;
     }
     array[preIndex + 1] = current;
 }
 return array;
}
快速排序
import java.util.Arrays;

//快速排序
public class 快速排序 {

 public static void main(String[] args) {
     int[] arr=new int[]{52, 49, 80, 36, 14, 58, 61, 97, 23, 75};
     quicksort qk=new quicksort();
     qk.arr=arr;
     int[] res = qk.sort(0, arr.length - 1);
     System.out.println(Arrays.toString(res));
 }

 static class quicksort{
     int[] arr;
     int[] sort(int low,int high){
         int i,j,t,tmp;
         if (low>high){
             return arr;
         }
         i=low;
         j=high;
         tmp = arr[low];//基准
         while (i!=j){
             //先从右向左找小于基准的数
             while (i<j && arr[j]>=tmp){
                 j--;
             }
             //再从左向右找大于基准的数
             while (i<j && arr[i]<=tmp){
                 i++;
             }
             //交换上边两个数的位置
             if(i<j){
                 t=arr[i];
                 arr[i]=arr[j];
                 arr[j]=t;
             }
             //循环交换位置,直到i==j退出循环,此时i的位置是基准该在的位置
         }
         //交换基准和循环得到的索引
         arr[low]=arr[i];//arr[low]就是tmp,这里不用再作临时变量了
         arr[i]=tmp;
         //递归处理基准左边数组
         sort(low,i-1);
         //递归处理基准右边数组
         sort(i+1,high);
         return arr;
     }
 }
}

希尔排序
public static int[] ShellSort(int[] array) {
 int len = array.length;
 int temp, gap = len / 2;
 while (gap > 0) {
     for (int i = gap; i < len; i++) {
         temp = array[i];
         int preIndex = i - gap;
         while (preIndex >= 0 && array[preIndex] > temp) {
             array[preIndex + gap] = array[preIndex];
             preIndex -= gap;
         }
         array[preIndex + gap] = temp;
     }
     gap /= 2;
 }
 return array;
}
递归排序
import org.junit.Test;

import java.util.Arrays;

public class 归并排序 {
 //1 两个有序数组a,b合并成c
 static int[] sum(int[] a,int alen,int[] b,int blen,int[] c){
     //初始化三个指针对应三个数组
     int i,j,k;
     i=j=k=0;
     //循环直到i或j越界
     while (i<alen && j<blen){
         if (a[i]<b[j]){
             //先放小的数,放完+1
             c[k++]=a[i++];
         }else {
             c[k++]=b[j++];
         }
     }
     //经过上边的循环后的i,j如果还有元素,直接放进去
     while (i<alen){
         c[k++]=a[i++];
     }
     while (j<blen){
         c[k++]=b[j++];
     }
     return c;
 }
 @Test
 public void a1() {
     int[] a={1,3,5,7,9};
     int[] b={2,4,6,8,10,12,14};
     int[] c=new int[a.length+b.length];
     int[] sum = sum(a, a.length, b, b.length, c);
     System.out.println(Arrays.toString(sum));
 }
 //2 归并排序
 //递归,将数组分为两组,判断是否有序,无序继续分两组,直到有序
 @Test
 public void a2(){
     int[] arr={4,5,2,3,6,8,0,9,1,7};
     //start=1从第二个元素开始比较
     int[] res = merge(arr, 1, arr.length);
     System.out.println(Arrays.toString(res));
 }
 //合并方法
 public int[] merge(int[] data,int start,int end){
     if (start<end){
         //取中间点作分组尝试
         int mid=(start+end)/2;
         //递归再二分
         merge(data,start,mid);
         merge(data,mid+1,end);
         //调用分组方法
         System.out.println(Arrays.toString(data));
         part(data,start,mid,end);

     }
     return data;
 }
 //分两组方法
 public void part(int[] data,int start,int mid,int end){
     //分两组A和B,长度分别为中间到左右端长度
     int lena=mid-start+1;//start从1开始,要把这个长度补上
     int lenb=end-mid;
     int[] A=new int[lena+1];//左数组
     int[] B=new int[lenb+1];//右数组

     //分别遍历数组A和B,将给定数组的左右元素分别插入对应位置
     for (int i = 0; i < lena; i++) {
         A[i] = data[i+start-1];
     }A[lena]=Integer.MAX_VALUE;//将数组A的最后一个元素赋值为最大整数
     System.out.println("A:"+Arrays.toString(A));
     for (int i = 0; i < lenb; i++) {
         B[i] = data[i+mid];
     }B[lenb]=Integer.MAX_VALUE;
     System.out.println("B:"+Arrays.toString(B));
     //此时左右两个数组已经是有序的,依次比较放入小数即可
     int m=0;
     int n=0;
     for (int i = start-1; i < end; i++) {
         if (A[m]>B[n]){
             //先放小的数
             data[i]=B[n++];
         }else {
             data[i]=A[m++];
         }
     }
     //最终返回的数组长度依然为初始长度,因此追加的两个最大整数会在A,B的最后被抛弃
 }
}

归并排序
/**
 * 归并排序
*
 * @param array
 * @return
*/
public static int[] MergeSort(int[] array) {
 if (array.length < 2) return array;
 int mid = array.length / 2;
 int[] left = Arrays.copyOfRange(array, 0, mid);
 int[] right = Arrays.copyOfRange(array, mid, array.length);
 return merge(MergeSort(left), MergeSort(right));
}
/**
     * 归并排序——将两段排序好的数组结合成一个排序数组
  *
     * @param left
     * @param right
     * @return
  */
public static int[] merge(int[] left, int[] right) {
 int[] result = new int[left.length + right.length];
 for (int index = 0, i = 0, j = 0; index < result.length; index++) {
     if (i >= left.length)
         result[index] = right[j++];
     else if (j >= right.length)
         result[index] = left[i++];
     else if (left[i] > right[j])
         result[index] = right[j++];
     else
         result[index] = left[i++];
 }
 return result;
}
堆排序
//声明全局变量,用于记录数组array的长度;
static int len;
/**
 * 堆排序算法
*
 * @param array
 * @return
*/
public static int[] HeapSort(int[] array) {
 len = array.length;
 if (len < 1) return array;
 //1.构建一个最大堆
 buildMaxHeap(array);
 //2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆
 while (len > 0) {
     swap(array, 0, len - 1);
     len--;
     adjustHeap(array, 0);
 }
 return array;
}
/**
     * 建立最大堆
  *
     * @param array
  */
public static void buildMaxHeap(int[] array) {
 //从最后一个非叶子节点开始向上构造最大堆
 for (int i = (len/2 - 1); i >= 0; i--) { //感谢 @让我发会呆 网友的提醒,此处应该为 i = (len/2 - 1) 
     adjustHeap(array, i);
 }
}
/**
 * 调整使之成为最大堆
*
 * @param array
 * @param i
*/
public static void adjustHeap(int[] array, int i) {
 int maxIndex = i;
 //如果有左子树,且左子树大于父节点,则将最大指针指向左子树
 if (i * 2 < len && array[i * 2] > array[maxIndex])
     maxIndex = i * 2;
 //如果有右子树,且右子树大于父节点,则将最大指针指向右子树
 if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex])
     maxIndex = i * 2 + 1;
 //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
 if (maxIndex != i) {
     swap(array, maxIndex, i);
     adjustHeap(array, maxIndex);
 }
}
计数排序
public static int[] CountingSort(int[] array) {
 if (array.length == 0) return array;
 int bias, min = array[0], max = array[0];
 for (int i = 1; i < array.length; i++) {
     if (array[i] > max)
         max = array[i];
     if (array[i] < min)
         min = array[i];
 }
 bias = 0 - min;
 int[] bucket = new int[max - min + 1];
 Arrays.fill(bucket, 0);
 for (int i = 0; i < array.length; i++) {
     bucket[array[i] + bias]++;
 }
 int index = 0, i = 0;
 while (index < array.length) {
     if (bucket[i] != 0) {
         array[index] = i - bias;
         bucket[i]--;
         index++;
     } else
         i++;
 }
 return array;
}
桶排序
public static ArrayList<Integer> BucketSort(ArrayList<Integer> array, int bucketSize) {
 if (array == null || array.size() < 2)
     return array;
 int max = array.get(0), min = array.get(0);
 // 找到最大值最小值
 for (int i = 0; i < array.size(); i++) {
     if (array.get(i) > max)
         max = array.get(i);
     if (array.get(i) < min)
         min = array.get(i);
 }
 int bucketCount = (max - min) / bucketSize + 1;
 ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);
 ArrayList<Integer> resultArr = new ArrayList<>();
 for (int i = 0; i < bucketCount; i++) {
     bucketArr.add(new ArrayList<Integer>());
 }
 for (int i = 0; i < array.size(); i++) {
     bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
 }
 for (int i = 0; i < bucketCount; i++) {
     if (bucketSize == 1) { // 如果带排序数组中有重复数字时  感谢 @见风任然是风 朋友指出错误
         for (int j = 0; j < bucketArr.get(i).size(); j++)
             resultArr.add(bucketArr.get(i).get(j));
     } else {
         if (bucketCount == 1)
             bucketSize--;
         ArrayList<Integer> temp = BucketSort(bucketArr.get(i), bucketSize);
         for (int j = 0; j < temp.size(); j++)
             resultArr.add(temp.get(j));
     }
 }
 return resultArr;
}
基数排序
public static int[] RadixSort(int[] array) {
 if (array == null || array.length < 2)
     return array;
 // 1.先算出最大数的位数;
 int max = array[0];
 for (int i = 1; i < array.length; i++) {
     max = Math.max(max, array[i]);
 }
 int maxDigit = 0;
 while (max != 0) {
     max /= 10;
     maxDigit++;
 }
 int mod = 10, div = 1;
 ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
 for (int i = 0; i < 10; i++)
     bucketList.add(new ArrayList<Integer>());
 for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
     for (int j = 0; j < array.length; j++) {
         int num = (array[j] % mod) / div;
         bucketList.get(num).add(array[j]);
     }
     int index = 0;
     for (int j = 0; j < bucketList.size(); j++) {
         for (int k = 0; k < bucketList.get(j).size(); k++)
             array[index++] = bucketList.get(j).get(k);
         bucketList.get(j).clear();
     }
 }
 return array;
}
二分查找
//二分查找法
public class 二分查找 {
 public static int a1(int key){
     //从升序排序的数组中查找KEY所在索引
     int [] arr=new int[]{0,1,2,3,4,5,6,7,8,9};
     //初始化查找的起点和终点
     int end=arr.length-1;
     int start=0;
     while (start<=end){
         //每次都从中间开始找
         int mid=(end+start)/2;
         if (arr[mid]==key){
             return mid;
         }else if (arr[mid]>key){
             //中间数大于key,就找比中间数小的
             end = mid;
         }else {
             start = mid;
         }
     }
     return -1;//表示未找到
 }

 public static void main(String[] args) {
     int i = a1(4);
     System.out.println(i);
 }

}

找出数组中第二大的数
import org.junit.Test;

//找出数组中第二大的数
public class 第二大的数 {
 //判断一个数组中第二大的数
 @Test
 public void a1(){
     String str="3 4 2 5 1 6 7 8";
     String[] strs = str.split("\\s");
     int[] arr=new int[strs.length];
     //转换字符串数组到整数数组
     for (int i = 0; i < arr.length; i++) {
         arr[i]=Integer.parseInt(strs[i]);
     }
     //初始化最大数为数组第一个数
     int max=arr[0];
     int sec=0;
     for (int i = 0; i < arr.length; i++) {
         //遍历数组,如果有数比第二大的数大,就进入交换
         if (arr[i]>sec){
             //如果这个数比第二和第一都大,就赋值第二大数为最大数,最大数为该数
             if (arr[i]>max){
                 sec=max;
                 max=arr[i];
             }else {
                 //如果只比第二大数大,比第一大数小,就直接赋值给第二大数
                 sec=arr[i];
             }
         }
     }
     System.out.println("最大的数:"+max+",第二大的数:"+sec);
 }

}

求一个整数的阶乘
//数的阶乘,递归
public class 阶乘 {
 public static int a1(int num){
     if (num<=0){
         return 0;
     }else if (num==1){
         return 1;
     }else {
         return num*a1(num-1);
     }
 }

 public static void main(String[] args) {
     int i = a1(3);
     System.out.println(i);
 }

}

斐波那契数列
//除第一个和第二个数外,任意一个数都可由前两个数相加得到
public class FibonacciDemo {
 public static ArrayList<Integer> fib(int size) {
     int a = 0;
     int b = 1;
     //中间结果标记
     int n = 0;
     //数组长度计数器
     int len = 0;
     ArrayList<Integer> nums = new ArrayList<>();
     while (len <= size) {
         len++;
         n = a + b;
         nums.add(n);
         a = b;
         b = n;
     }
     return nums;
 }

 public static void main(String[] args) {
     ArrayList<Integer> fibList = fib(10);
     System.out.println(fibList);
 }
}
雪花算法
public class IdWorker {

	//因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。

	//机器ID  2进制5位  32位减掉1位 31个
	private long workerId;
	//机房ID 2进制5位  32位减掉1位 31个
	private long datacenterId;
	//代表一毫秒内生成的多个id的最新序号  12位 4096 -1 = 4095 个
	private long sequence;
	//设置一个时间初始值    2^41 - 1   差不多可以用69年
	private long twepoch = 1585644268888L;
	//5位的机器id
	private long workerIdBits = 5L;
	//5位的机房id
	private long datacenterIdBits = 5L;
	//每毫秒内产生的id数 2 的 12次方
	private long sequenceBits = 12L;
	// 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
	private long maxWorkerId = -1L ^ (-1L << workerIdBits);
	// 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内
	private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

	private long workerIdShift = sequenceBits;
	private long datacenterIdShift = sequenceBits + workerIdBits;
	private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
	private long sequenceMask = -1L ^ (-1L << sequenceBits);
	//记录产生时间毫秒数,判断是否是同1毫秒
	private long lastTimestamp = -1L;
	public long getWorkerId(){
		return workerId;
	}
	public long getDatacenterId() {
		return datacenterId;
	}
	public long getTimestamp() {
		return System.currentTimeMillis();
	}



	public IdWorker(long workerId, long datacenterId, long sequence) {

		// 检查机房id和机器id是否超过31 不能小于0
		if (workerId > maxWorkerId || workerId < 0) {
			throw new IllegalArgumentException(
					String.format("worker Id can't be greater than %d or less than 0",maxWorkerId));
		}

		if (datacenterId > maxDatacenterId || datacenterId < 0) {

			throw new IllegalArgumentException(
					String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId));
		}
		this.workerId = workerId;
		this.datacenterId = datacenterId;
		this.sequence = sequence;
	}

	// 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id
	public synchronized long nextId() {
		// 这儿就是获取当前时间戳,单位是毫秒
		long timestamp = timeGen();
		if (timestamp < lastTimestamp) {

			System.err.printf(
					"clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
			throw new RuntimeException(
					String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
							lastTimestamp - timestamp));
		}

		// 下面是说假设在同一个毫秒内,又发送了一个请求生成一个id
		// 这个时候就得把seqence序号给递增1,最多就是4096
		if (lastTimestamp == timestamp) {

			// 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来,
			//这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
			sequence = (sequence + 1) & sequenceMask;
			//当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
			if (sequence == 0) {
				timestamp = tilNextMillis(lastTimestamp);
			}

		} else {
			sequence = 0;
		}
		// 这儿记录一下最近一次生成id的时间戳,单位是毫秒
		lastTimestamp = timestamp;
		// 这儿就是最核心的二进制位运算操作,生成一个64bit的id
		// 先将当前时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后12 bit
		// 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型
		return ((timestamp - twepoch) << timestampLeftShift) |
				(datacenterId << datacenterIdShift) |
				(workerId << workerIdShift) | sequence;
	}

	/**
	 * 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
	 * @param lastTimestamp
	 * @return
	 */
	private long tilNextMillis(long lastTimestamp) {

		long timestamp = timeGen();

		while (timestamp <= lastTimestamp) {
			timestamp = timeGen();
		}
		return timestamp;
	}
	//获取当前时间戳
	private long timeGen(){
		return System.currentTimeMillis();
	}

	/**
	 *  main 测试类
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println(1&4596);
		System.out.println(2&4596);
		System.out.println(6&4596);
		System.out.println(6&4596);
		System.out.println(6&4596);
		System.out.println(6&4596);
//		IdWorker worker = new IdWorker(1,1,1);
//		for (int i = 0; i < 22; i++) {
//			System.out.println(worker.nextId());
//		}
	}
}
穷举算法
从所有可能情况中搜索正确答案 
1. 对于一种可能情况,计算其结果。 
2. 判断结果是否满足,如不能满足者执行第一步来搜索下一个可能的情况;如满足则表示选找到一个正确答案。

穷举算法实例
仅有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何?
int Qiongju(int head,int foot)
{   
 int i,j,chicken,rabbit;
 for(i=0;i<=head;i++)
 {
     j = head-i;
     if(i*2+j*4==foot)
     {
         chicken = i;
         rabbit = j;
         return 1;
     }
 }
 return 0;
}
逆推算法
递推算法是一种理性思维模式的代表,其根据已有的数据和关系,逐步推到而得到结果。对推算法的执行过程:

根据已知结果和关系,求解中间结果
判定是否但到要求,若没有继续执行第一步,若有则表示找到一个正确结果 
递推算法往往需要知道答案和问题之间的实际逻辑关系。再许多数学问题中往往都有着明确的计算公式可以遵循,因此可以采用递推算法来实现。

递推算法实例
斐波那契数列:兔子产仔问题 
题目:如果一对两个月大的兔子以后每个月都可以生一对小兔子,而刚出生的兔子两个月后才可以生小兔子,假定一年内没有兔子死亡事件,那么一年后共有多少对兔子呢?
int Fibonacci(int n)
{
 int t1,t2;
 if(n==1||n==2)
 {
     return 1;
 }
 else
 {
     t1 = Fibonacci(n-1);   //递归调用
     t2 = Fibonacci(n-2);
     return t1 + t2;
 }
}
递归算法
递归算法即在程序中不反复调用自身达到解决问题的方法,是一个方法在其方法体内调用自身方法调用方式。在递归中主方法又是被调方法。执行递归将反复调用其自身。每调用一层就进入新的一层。 
递归调用分为两种情况:

直接递归,即在方法中调用方法本身
间接递归,即间接地调用一个方法 
编写递归方法时,必须使用if语句强制在未执行递归前返回。
递归算法实例
递归算法常用于有明显递推性质的问题和一些数学问题。 
问题:求阶乘 
n!=n*(n-1)(n-2)(n-3)………*2*1 
(n-1)!=(n-1)(n-2)(n-3)………*2*1 
得到递推公式: 
n! =n*(n-1)!
 
-->算法实现
long fact(int n)
{
 if(n<=1)
 return 1;
 else
 return n*fact(n-1);
}
分治算法
分治算法就是把一个复杂问题分为规模较小的,计算简单的小问题,然后综合小问题得到最后答案的思想。分治算法执行过程如下:

对于一个规模为N的问题,若给问题比较容易解决,则直接解决;否则执行下面的步骤。
将该问题分解为M个规模较小的问题,这些问题相互独立,并且与原问题相互独立。
递归这些小问题
然后,将各个小问题合并得到完问题的解
分治算法实例
题目:一个袋子里有三十个硬币,其中有一枚假币,并且假币和真币一某一样,肉眼很难分辨,目前只知道假币比真币轻一点,请问如何区分假币?

算法分析
首先为每个硬币编号,然后然后将所有硬币等分为两份,放在天平两边。
再将较轻的那一份等分为两份重复上述方法
直到剩下两个硬币,较轻的一个就是假币
int FalseCoin(int coin[],int low,int high)
{
 int i,sum1,sum2,sum3;
 int re = 0;
 sum1 = sum2 = sum3 = 0;
 if(low+1==high)
 {
     if(coin[low]<coin[high])
     {
         re = low + 1;
         return re;
     }
     else 
     {
         re = high +1;
         return re;
     }
 }
 if((high-low+1)%2==0)   //n是偶数
 {
     for(i=low;i<=low+(high-low)/2;i++)
     {
         sum1= sum1+coin[i];
     }
     for(i=low+low+(high-low)/2;i<=high;i++)
     {
         sum2= sum2+coin[i];
     }
     if(sum1>sum2)
     {
         re=FalseCoin(coin,low+(high-low)/2,high);
         return re;
     }
     else if(sum1<sum2)
     {
         re=FalseCoin(coin,low,low+(high-low)/2);
         return re;
     }
 }
 else
 {
     for(i=low;i<=low+(high-low)/2-1;i++)
     {
         sum1= sum1+coin[i];
     }
     for(i=low+low+(high-low)/2+1;i<=high;i++)
     {
         sum2= sum2+coin[i];
     }
     sum3=coin[low+(high-low)/2];
     if(sum1>sum2)
     {
         re=FalseCoin(coin,low+(high-low)/2+1,high);
         return re;
     }
     else if(sum1<sum2)
     {       
         re=FalseCoin(coin,low,low+(high-low)/2-1);
         return re;
     }
     else
     {
         re=low+(high-low)/2+1;
         return re;
     }
 }
 return re;
}

-->分治算法_合并排序
public class 分治_合并排序 {
	/**
	 * 函数说明:在数组被拆分以后进行合并
	 */
	static void Merge(int a[], int left, int middle, int rigth) {
		//定义左端数组大小
		int n1 = middle - left+1;
		int n2 = rigth - middle;
		
		//初始化数组,分配内存
		int bejin[] = new int[n1];
		int end[] = new int[n2];
		
		//数组赋值
		for(int i = 0; i < n1; i++)
			bejin[i] = a[left + i];
			
		for(int i = 0; i < n2; i++) 
			end[i] = a[middle+1+i];
		
		//用key做原数组索引,没调用一次函数重新给原数组付一次值
		int i = 0, j = 0, key;
		for(key = left; key <= rigth; key++){
			
			if(n1>i&&n2>j&&i < n1 && bejin[i] <= end[j])
				a[key] = bejin[i++];
			else if(n1>i&&n2>j&&j < n2 && bejin[i] >= end[j])
				a[key] = end[j++];	
			else if(i == n1 && j < n2)
				a[key] = end[j++];
			else if(j == n2 && i < n1)
				a[key] = bejin[i++];	
		}
	}
	/**
	 * 差分数组区间,不断分支
	 */
	static void MergeSort(int a[],int left,int rigth) {
		int middle=0;
		if(left<rigth) {
			middle =(rigth+left)/2;
			MergeSort(a, left, middle);
			MergeSort(a, middle+1, rigth);
			Merge(a, left, middle, rigth);
		}
	}
	public static void main(String[] args) {
		int a[]= {85,3,52,9,7,1,5,4};
		MergeSort(a, 0,7); 
		for(int i=0;i<8;i++) {
			System.out.print(" "+a[i]);
		}
		
	}
}

-->分治算法_快速排序
public class 分治_快速排序 {
	/**
	 *交换函数,i,j为数组索引
	 */
	static void swap(int A[], int i, int j)
	{
	    int temp = A[i];
	    A[i] = A[j];
	    A[j] = temp;
	}
	/**
	 * 选取一个关键字(key)作为枢轴,一般取整组记录的第一个数/最后一个,这里采用选取序列最后一个数为枢轴。
	 * 设置两个变量left = 0;right = N - 1;
	 * 从left一直向后走,直到找到一个大于key的值,right从后至前,直至找到一个小于key的值,然后交换这两个数。
	 * 重复第三步,一直往后找,直到left和right相遇,这时将key放置left的位置即可。
	 * @return
	 */
	static int PartSort(int[] array,int left,int right)
	{
	    int key = array[right];//定义基准 
	    int count=right;//保存rigth值
	    while(left < right)//防止数组越界
	    {
	        while(left < right && array[left] <= key)
	        {
	            ++left;
	        }
	        while(left < right && array[right] >= key)
	        {
	            --right;
	        }
	        swap(array,left,right);
	    }
	    swap(array,right,count);
	    return right;
	}
	/**
	 *分治思想,递归调用
	 */
	static void QuickSort(int array[],int left,int right)
	{
	    if(left >= right)//表示已经完成一个组
	    {
	        return;
	    }
	    int index = PartSort(array,left,right);//枢轴的位置
	    QuickSort(array,left,index - 1);
	    QuickSort(array,index + 1,right);
	}
	public static void main(String[] args) {
		int a[]= {1,5,-5,54,15,67,16,23};
		QuickSort(a,0,7);
		for(int i=0;i<a.length;i++) {
			System.out.print(" "+a[i]);
		}
	    System.out.print("\n");
	}
}
贪心算法
import java.util.*;
public class 贪心算法_prim算法 {
	static int MAX = Integer.MAX_VALUE;
	public static void main(String[] args) {
		//定义无向图矩阵
		int[][] map = new int[][] {
				{ 0, 1, 6, 2},
				{ 1, 0, 3, 2},
				{ 6, 3, 0, 1},
				{ 2, 2, 1, 0}
				};
		prim(map, map.length);
	}
	public static void prim(int[][] graph, int n){
			//定义节点名字
	        char[] c = new char[]{'A','B','C','D'};        
	        int[] lowcost = new int[n];  //到新集合的最小权 
	        int[] mid= new int[n];//存取前驱结点
         List<Character> list=new ArrayList<Character>();//用来存储加入结点的顺序
	        int i, j, min, minid , sum = 0;
	        //初始化辅助数组
	        for(i=1;i<n;i++)
	        {
	        	lowcost[i]=graph[0][i];
	        	mid[i]=0;
	        }
	        list.add(c[0]);
         //一共需要加入n-1个点
	        for(i=1;i<n;i++)
	        {
	        	 min=MAX;
	        	 minid=0;
	        	 //每次找到距离集合最近的点
	        	 for(j=1;j<n;j++)
	        	 {
	        		 if(lowcost[j]!=0&&lowcost[j]<min)
	        		 {
	        			 min=lowcost[j];
	        			 minid=j;
	        		 }
	        	 }
	        	 if(minid==0) return;
	        	 list.add(c[minid]);
	        	 lowcost[minid]=0;
	        	 sum+=min;
	        	 System.out.println(c[mid[minid]] + "到" + c[minid] + " 权值:" + min);
	        	 //加入该点后,更新其它点到集合的距离
	        	 for(j=1;j<n;j++)
	        	 {
	        		 if(lowcost[j]!=0&&lowcost[j]>graph[minid][j])
	        		 {
	        			 lowcost[j]=graph[minid][j];
	        			 mid[j]=minid;	 
	        		 }
	        	 }
	        	 System.out.print("\n");
	        }
	        System.out.println("sum:" + sum);
	    }
}
动态规划算法
public class 动态规划_背包问题 {
public static void main(String[] args) {
	//物品价值,重量,和背包承重
	int v[]={0,8,10,6,3,7,2};
	int w[]={0,4,6,2,2,5,1};
	int c=12;
	
	//定义二位数组动态规划背包价值和重量
	int m[][]=new int[v.length][c+1];
	for (int i = 1; i <v.length; i++) {
		for (int j = 1; j <=c; j++) {
			if(j>=w[i])
				m[i][j]=m[i-1][j-w[i]]+v[i]>m[i-1][j]?m[i-1][j-w[i]]+v[i]:m[i-1][j];
			else
				m[i][j]=m[i-1][j];
		}
	}
	int max=0;
	for (int i = 0; i <v.length; i++) {
		for (int j = 0; j <=c; j++) {
			if(m[i][j]>max)
				max=m[i][j];
		}
	}
	System.out.println(max);
}
}
回溯算法
public class 回溯法_求子集问题 {
	    private static int[] s = {2,2,3};  
	    private static int n = s.length;  
	    private static int[] x = new int[n];       
	    /** 
	     * 输出集合的子集 
	     * @param limit  决定选出特定条件的子集 
	     * 注:all为所有子集,num为限定元素数量的子集, 
	     *    sp为限定元素奇偶性相同,且和小于8。 
	     */  
	    public static void all_subset(String limit){  
	        switch(limit){  
	        case "all":backtrack(0);break;  
	        case "num":backtrack1(0);break;  
	        case "sp":backtrack2(0);break;  
	        }  
	    }        
	    /** 
	     * 回溯法求集合的所有子集,依次递归 
	     * 注:是否回溯的条件为精髓 
	     * @param t 
	     */  
	    private static void backtrack(int t){  
	        if(t >= n) 
	        	output(x);      
	        else  
	            for (int i = 0; i <= 1; i++) {  
	                x[t] = i;  
	                backtrack(t+1);  
	            }        
	    }  
	    /** 
	     * 回溯法求集合的所有(元素个数小于4)的子集,依次递归 
	     * @param t 
	     */  
	    private static void backtrack1(int t){  
	        if(t >= n)  
	            output(x);  
	        else  
	            for (int i = 0; i <= 1; i++) {  
	                x[t] = i;  
	                if(count(x, t) < 4)  
	                    backtrack1(t+1);  
	            }       
	    }  
	  
	    /** 
	     * (剪枝) 
	     * 限制条件:子集元素小于4,判断0~t之间已被选中的元素个数, 
	     *        因为此时t之后的元素还未被递归,即决定之后的元素 
	     *        是否应该被递归调用 
	     * @param x 
	     * @param t 
	     * @return 
	     */  
	    private static int count(int[] x, int t) {  
	        int num = 0;  
	        for (int i = 0; i <= t; i++) {  
	            if(x[i] == 1){  
	                num++;  
	            }  
	        }  
	        return num;  
	    }  
	    /** 
	     * 回溯法求集合中元素奇偶性相同,且和小于8的子集,依次递归 
	     * @param t 
	     */  
	    private static void backtrack2(int t){  
	        if(t >= n)  
	            output(x);  
	        else  
	            for (int i = 0; i <= 1; i++) {  
	                x[t] = i;  
	                if(legal(x, t))  
	                    backtrack2(t+1);  
	            }   
	    }  
	    /** 
	     * 对子集中元素奇偶性进行判断,还需元素的数组和小于8 
	     * @param x 
	     * @param t 
	     * @return 
	     */  
	    private static boolean legal(int[] x, int t) {  
	        boolean bRet = true;   //判断是否需要剪枝  
	        int part = 0;  //奇偶性判断的基准  
	          
	        for (int i = 0; i <= t; i++) {  //选择第一个元素作为奇偶性判断的基准  
	            if(x[i] == 1){  
	                part = i;  
	                break;  
	            }  
	        }    
	        for (int i = 0; i <= t; i++) {  
	            if(x[i] == 1){  
	                bRet &= ((s[part] - s[i]) % 2 == 0);  
	            }       
	        }  
	        int sum = 0;  
	        for(int i = 0; i <= t; i++){  
	            if(x[i] == 1)  
	                sum += s[i];  
	        }  
	        bRet &= (sum < 8);   
	        return bRet;  
	    }  
	    /** 
	     * 子集输出函数 
	     * @param x 
	     */  
	    private static void output(int[] x) {  
	        for (int i = 0; i < x.length; i++) {  
	            if(x[i] == 1){  
	                System.out.print(s[i]);  
	            }  
	        }  
	        System.out.println();     
	    }  
	  public static void main(String[] args) {
		  all_subset("all");
	}
} 
分支限界算法
import java.util.Collections;

import java.util.LinkedList;

public class 分支界限法_求最大承重问题 {
	LinkedList<HeapNode> heap;
	public static class BBnode{
		BBnode parent;//父结点
		boolean leftChild;//左儿子结点标志
		//构造方法
		public BBnode(BBnode par,boolean ch){
			parent=par;
			leftChild=ch;
		}
	}
	/**
	 * 输出函数,做调试用
	 * @param list
	 */
	public static void printReverse(LinkedList<HeapNode> list){
		for (int i=0;i<list.size();i++) {
			HeapNode aBnode=list.get(i);
			System.out.print("#"+aBnode.uweight+"#"+aBnode.level+" ");
     }
		
		}
	/*
	 * 最大优先队列中存储的活结点类型为HeapNode
	 */
	public static class HeapNode implements Comparable{
		BBnode liveNode;
		int uweight;//活结点优先级(上界)
		int level;//活结点在子集树种所处的层序号
		//构造函数
		public HeapNode(BBnode node,int up,int lev){
			liveNode=node;
			uweight=up;
			level=lev;
		}
		@Override
		public int compareTo(Object x) {//升序排列
			int xu=((HeapNode)x).uweight;
			if(uweight<xu) return -1;
			if(uweight==xu) return 0;
			return 1;
		}
		public boolean equals(Object x){
			return uweight==((HeapNode)x).uweight;
		}
	}
	public void addLiveNode(int up,int lev,BBnode par,boolean ch){
		//将活结点加入到表示活结点优先队列的最大堆H中
		BBnode b=new BBnode(par,ch);
		HeapNode node=new HeapNode(b,up,lev);
		heap.add(node);
		Collections.sort(heap);
	}
	public int maxLoading(int[] w,int c,int[] bestx){
		int count=0;
		//优先队列式分支限界法,返回最优重量,bestx返回最优解
		heap=new LinkedList<HeapNode>();
		int n=w.length-1;
		BBnode e=null;//当前扩展结点
		int i=1;//当前扩展结点所处的层
		int ew=0;//扩展结点所对应的载重量
		//定义剩余重量数组r
		int[] r=new int[n+1];
		for(int j=n-1;j>0;j--) {
			r[j]=r[j+1]+w[j+1];
		}
		//搜索子集空间树
		while(i!=n+1){
			//非叶结点
			//检查当前扩展结点的儿子结点
			if(ew+w[i]<=c){
				//左儿子结点为可行结点
				addLiveNode(ew+w[i]+r[i],i+1,e,true);
			}
			//右儿子结点总为可行结点
			addLiveNode(ew+r[i],i+1,e,false);
			//printReverse(heap);
			//取下一个结点
			HeapNode node=heap.pollLast();
			i=node.level;
			e=node.liveNode;
			ew=node.uweight-r[i-1];
		}
		
		//输出
		for(int j=0;j<n;j++){
			bestx[j]=(e.leftChild)?1:0;
			e=e.parent;
		}
		for(int j=n-1;j>=0;j--){
			System.out.print(bestx[j]+" ");
		}
		System.out.println();
		return ew;
	}
	public static void main(String[] args) {
		int n=4;
		int c=70;
		int w[]={0,26,60,22,18};//下标从1开始
		int[] bestx=new int[n+1];
		分支界限法_求最大承重问题 b=new 分支界限法_求最大承重问题();
		System.out.println("最优装载顺序为(1表示装入,0表示未装入):");
		int ew=b.maxLoading(w, c, bestx);
		System.out.println("最优装载重量为:"+ew);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值