JAVA算法结构(简单)

~~~~~~~持续更新~~~~~~

目录

一、位运算

  (1)、二进制中1 的个数

  (2)、是不是二的整数次方

(3)、将整数的奇偶数位互换

(4)、0~1之间的浮点实数的二进制表示

(5)、出现K次与出现1次

二、 查找与排序

1、递归、查找与排序补充

(1) 求一个数的阶乘

(2) 、数组求和

 (3)、字符串的翻转

 (4)、斐波那契数列

   (5)、最大公约数 

 (6)、 递归改排序

 (7)、汉诺塔游戏

2、如何评估算法性能

(1)、 顺序查找、二分查找分析:n与lgn

(2)、冒泡、插入、选择排序的分析:n^与nlgn 

(3)、典型递归算法的性能分析


一、位运算

(1)、二进制中1 的个数

请实现一个函数,输入一个整数,输出该数二进制表示中1 的个数
例:9的二进制表示为1001,有两个1

“1”左移做“&”运算
方法一:

 


import java.util.Scanner;

public class 二进制中的1数 {

    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        System.out.println(Integer.toString(N,2));
        
        int count=0;
        //比对每一位
        for(int i=0;i<32;i++) {
            if((N &(1<<i))==(1<<i)) {
                count++;
            }
        }
        System.out.println(count);


方法二:


int count =0;
        while(N!=0) {
            N=((N-1)& N);
            count++;
        }
        System.out.println(count);
    }

(2)、是不是二的整数次方

用一条语句判断一个整数是不是2 的整数次方

 他的二进制数中只有一个1

Scanner sc=new Scanner(System.in);
		int s=sc.nextInt();
		Integer.toString(s,2);
		if(((s-1)&s)==0) {
			System.out.println("是");
		}else {
			System.out.println("不是");
		}
	}
}

( 3)、将整数的奇偶数位互换

 

public static void main(String[] args) {

		Scanner sc=new Scanner(System.in);
		int s=sc.nextInt();
		Integer.toString(s,2);
		int ou=s & (0xaaaaaaaa);
		int ji=s & (0x55555555);
		s=(ou>>1)^(ji<<1);
		System.out.println(s);
	}
}

 (4)、0~1之间的浮点实数的二进制表示

给定一个介于0~1 之间的实数,(0.625),类型是double类型的打印他的二进制表示0.101

如果该数字无法精确的用32位以内的二进制表示,则打印“ERROR”

import java.util.Scanner;

public class 二进制小数 {

	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入一个0到1的浮点数");
		double num=sc.nextDouble();
		StringBuilder sb=new StringBuilder("0.");
		while(num>0) {
			double r=num*2;
			if(r>=1) {
				sb.append("1");
				//消除掉整数部分
				num=r-1;
			}else {
				sb.append("0");
				num=r;
			}
			if(sb.length()>34) {
				System.out.println("ERRPR");
				return;
			}
		}
		System.out.println(sb.toString());
	}
	}

(5)、出现K次与出现1次

数组中只有一个数出现了一次,其他的数出现了K次,请输出只出现一次的数

2个相同的2进制数做不进位加法,结果为0;

10个相同的10进制数做不进位加法,结果为0; 

k个相同的k进制数做不进位加法,结果为0;

package Demo;

public class 出现k次与出现1次 {
	public static void main(String[] args) {
int[] arr={2,2,2,9,7,7,7,3,3,3,6,6,6,0,0,0};
		
		int len = arr.length;
		char[][] KRadix = new char[len][]; //字符二维数组存取每一个数的三进制的每一位
		int k =3;
		
		//记录转成k进制数的最长位数,用来看最后需要计算多少列
		int maxLen = 0;
		//转成k进制数字
		//对于每个数字
		for(int i = 0;i<len;i++)
		{
			//求每个数字的三进制字符串并反转,然后转为字符数组    目的的将每个k进制数的低位对齐(因为转成k进制数的位数不同)
			KRadix[i] = new StringBuilder(Integer.toString(arr[i], k)).reverse().toString().toCharArray();
			if(KRadix[i].length > maxLen)
			{
				maxLen = KRadix[i].length;
			}
		}
		
		//存放做完不进位加法每一位和
		int[] resArr=new int[maxLen];
		//做不进位加法
		for(int i = 0;i<len;i++)
		{
			//不进位加法
			for(int j = 0;j<maxLen;j++)
			{
				//如果j大于等于当前数组长度的话,就需要补0
				if(j >=KRadix[i].length)
				{
					resArr[j]+=0;
				}else
				{
					resArr[j] += (KRadix[i][j]-'0');
				}
			}
		}
		int res = 0;
		for(int i = 0;i<maxLen;i++)
		{
			res+=(resArr[i]%k)*(int)(Math.pow(k, i));
		}
		System.out.println(res);
	}
}

二、 查找与排序

1、递归、查找与排序补充

找重复:n*(n-1)的阶乘,求n-1d阶乘是一个原问题的重复(规模更小)——子问题

找变化:变化的量应该作为参数

找边界:找出口

(1) 求一个数的阶乘

求n的阶乘

public class 求n的阶乘 {

	public static void main(String[] args) {
		System.out.println(f1(10));
	}
	
	static int f1(int n) {
		if(n==1) {
			return 1;
		}
	     return n*f1(n-1);     
	}
}

(2) 、数组求和

数组求和

public static void main(String[] args) {
		int res= f3(new int [] {1,2,3,4,5},0);
		System.out.println(res);
	}
	
static int f3(int[] arr,int begin) {
		if(begin==arr.length-1) {
			return arr[begin];
		}
		return arr[begin]+f3(arr,begin+1);
	}
	
}

 (3)、字符串的翻转

public static void main(String[] args) {
		System.out.println(reversr("asdfgh",5));
	}
	static String reversr(String str,int end) {
		if(end==0) {
			return ""+str.charAt(0);
		}
		return str.charAt(end)+reversr(str, end-1);
	}

 (4)、斐波那契数列

 

public class 斐波那契数列 {

	public static void main(String[] args) {
		System.out.println(fib(6));
	}
	static int fib(int n) {
		if(n==1||n==2) {
			return 1;
		}
		return fib(n-1)+fib(n-2);
	}
}

(4)、最大公约数 

public class 最大公约数 {

	public static void main(String[] args) {

		System.out.println(god(18,9));
	}
	static int god(int m,int n) {
		if(n==0) {
			return m;
		}
		return god(n,m%n);
	}
}

(5)、 递归改排序

 

static void insertSort(int [] arr,int k) {
		if(k==0) {
			return ;
		}
		//对k-1个元素排序
		insertSort(arr,k-1);
		//把位置k的元素插入到排序部分
		int x=arr[k];
		int index=k-1;
		while(index>=1&&x<arr[index]) {
			arr[index+1]=arr[index];
			index--;
		}
		arr[index]=x;
	}

 (6)、汉诺塔游戏

public class 汉诺塔游戏 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		printHannuoTower(3,"a","b","c");
		
	}

	/*
	 * 将N个盘子从source移动到target的路径打印
	 * N 初始的N个盘子从小到大,N是最大编号
	 * form 原始柱子
	 * to 辅助的柱子
	 * help 目标柱子
	 */
	static void printHannuoTower(int N,String form,String to,String help) {
		if(N==1) {
			System.out.println("将编号为"+N+"从"+form+"移动到"+to);
			return;
		}
		printHannuoTower(N-1,form,help,to);//先把前N-1个盘子挪到辅助空间上
		System.out.println("将编号为"+N+"从"+form+"移动到"+to);//N可以顺利到达target
		printHannuoTower(N-1,help,to,form);//让N-1从辅助空间回到源空间上
	}
}

斐波那契数列问题
    等价于两个子问题:
     求前一项求前二项两项求和
汉诺塔
  1-N从A移动到B,C作为辅助
      等价于;
       1~N-1从A移动到CB为辅助

       把N从A移动到B
      1--N-1从C移动到B为辅助
全范围内二分查找
    等价于三个子问题:
      左边找(递归)中间比
      右边找(递归)
注意:左查找和右查找只选其一

2、如何评估算法性能

如何评估一个算法性能?什么是大O表示法?

数组有序,顺序查找和二分查找哪个更快,快多少?

- n!的弱上界是n^n因此增长速度非常快,这意味着单位时间内可求解的问题很小,换言之,超慢
2^n这样的指数函数增长非常快,这种算法可以认为超慢
0(n2)和O(n3)增长很快,算法很慢,至少优化到nlgn,O(n2)的有冒泡排序,直接插入排序,选择排序
nlgn可以认为是及格的算法吧,一般分治法可以缩小层数为1gn,而每层的复杂度一般为O(n),例如归并排序算法、快速排序算法
0(n)叫做线性算法,这种算法比较优秀,或者问题本身比较简单,比如求连续求和最大子数组的线性解
0(sqrt(n))当然比O(n)更快,不是没有,但这种很少
- 1gn就是很优秀的算法了,比如二分查找法,但是这种算法往往对输入数据的格式是有要求的,二分查找要求输入数据有序
还有一种是常量,无论规模怎么扩大,都花固定时间,这是为数极少的效率最高的算法了,多数是数据很规则

(1)、 顺序查找、二分查找分析:n与lgn

直观感受性能差别

public class 顺序查找与二分查找 {

	public static void main(String[] args) {

		int [] x=new int[10000*10000];
		for(int i=0;i<x.length;i++) {
			x[i]=i+1;
		}
		int target=10000*10000;
		
		long now =System.currentTimeMillis();
		int index=brinarySearch(x,0,x.length-1,target);
		System.out.println(System.currentTimeMillis()-now+"ms");
		System.out.println(target+"所在位置为:"+index);
		
		
		//调用顺序查找
		now =System.currentTimeMillis();
		index=search(x,target);
		System.out.println(System.currentTimeMillis()-now+"ms");
	}

	//二分查找
	private static int brinarySearch(int arr[],int low,int high,int key) {
		while(low<=high) {
			int mid=low+((high-low)>>1);//(high-low)>>1,防止溢出,移位也更高,同时,每次循环都要更新
			int midVal=arr[mid];
			
			if(midVal<key) {
				low=mid+1;
			}else if(midVal>key) {
				high=mid-1;
			}else {
				return mid;//key found
			}
		}
		return -(low+1);//key not found
		
	}
	
	//顺序查找
	private static int  search(int arr[],int key) {
		for(int i=0;i<arr.length;i++) {
			if(arr[i]==key) {
			return i;
			}
		}
		return -1;
	}
}

 0ms
100000000所在位置为:99999999
58ms

(2)、冒泡、插入、选择排序的分析:n^与nlgn 

直观感受性能差别:和Arrays.sort()的执行时间对比

冒泡:

 插入:

 选择:

 

import java.util.Arrays;

public class 冒泡插入排序 {

	public static void main(String[] args) {
		//比较冒泡排序,选择排序,快速排序的效率
			int[] arr = generateRandomArray(100000, 100000);
			int[] a = copyArray(arr);   //冒泡排序
			int[] b = copyArray(arr);   //选择排序
			int[] c = copyArray(arr);   //快速排序
			int[] d = copyArray(arr);   //插入排序
			
			
			long start = System.currentTimeMillis();
			BubbleSort(a);
			long end = System.currentTimeMillis();
			System.out.println("冒泡排序所用时间:"+(end-start)+"ms");
	       
			
			long start1 = System.currentTimeMillis();
			SelectionSort(b);
			long end1 = System.currentTimeMillis();
			System.out.println("选择排序用时:"+(end1-start1)+"ms");
			
			long start2 = System.currentTimeMillis();
			InsertionSort(d);
			long end2 = System.currentTimeMillis();
			System.out.println("插入排序用时:"+(end2-start2)+"ms");
			
			long start3 = System.currentTimeMillis();
			QuickSort(c);
			long end3 = System.currentTimeMillis();
			System.out.println("Arrays.sort()用时:"+(end3-start3)+"ms");
			
		
		}
		
		/**冒泡排序*/
		public static void BubbleSort(int[] arr) {
			int temp;
			for(int i = 0;i<arr.length-1;i++) {
				for(int j = 0;j<arr.length-1-i;j++) {
					if(arr[j]>arr[j+1]) {
						temp = arr[j+1];
						arr[j+1] = arr[j];
						arr[j] = temp;
					}
				}
			}
		}
		
		/**选择排序*/
		public static void SelectionSort(int[] arr) {
			int temp;
			for(int i = 0;i<arr.length;i++) {
	        	int min = arr[i];
	            int minIndex = i;
	        	for(int j = i+1;j<arr.length;j++) {
	        		if(arr[j]<min) {
	        			min = arr[j];
	        			minIndex = j;
	        		}
	        	}
	        	temp = arr[i];
	        	arr[i] = arr[minIndex];
	        	arr[minIndex] = temp;
	        }
		}
		/**插入排序*/
		 public static void InsertionSort(int[] arr){
		        int j;
		        for (int i=1;i<arr.length;i++){
		            int t=arr[i];
		            for (j=i;j>0&&t<arr[j-1];j--){
		                arr[j]=arr[j-1];
		            }
		            arr[j]=t;
		        }
		    }


		/**快递排序*/
		public static void QuickSort(int[] arr) {
			Arrays.sort(arr);
		}
		
		/**
		 * 生成随机数组
		 * @param maxSize 最大长度
		 * @param maxValue 最大值
		 * @return
		 */
		public static int[] generateRandomArray(int maxSize, int maxValue) {
			int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
			for (int i = 0; i < arr.length; i++) {
				arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
			}
			return arr;
		}
		
		/**复制数组*/
		public static int[] copyArray(int[] arr) {
			if (arr == null) {
				return null;
			}
			int[] res = new int[arr.length];
			for (int i = 0; i < arr.length; i++) {
				res[i] = arr[i];
			}
			return res;
		}
	
	}

冒泡排序所用时间:19141ms
选择排序用时:3029ms
插入排序用时:3173ms
Arrays.sort()用时:101ms

(3)、典型递归算法的性能分析

例题

 1、旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转,输入一个递增排序的数组的一个旋转,输出的旋转数组的最小元素,例如数组{3、4、5、1、2}为{1、2、3、4、5}的一个旋转,该数组的最小值为1.

public class 旋转数组的最小数字 {

	public static void main(String[] args) {

		int [] arr= {3,4,5,1,2};
		int res=min(arr);
		System.out.println(res);
//		arr=new int[] {2,3,4,5,6};
//		res=min(arr);
//		arr=new int[] {1,0,1,1,1};
//		res=min(arr);
//		System.out.println(res);
	}
	static int min(int[] arr) {
		int begin =0;
		int end=arr.length-1;
		//考虑没有旋转这种特殊的旋转
		if(arr[begin]<arr[end]) return arr[begin];
		//begin 和end指向相邻的元素,退出
		while(begin+1<end) {
			int mid=begin+((end-begin)>>1);
			//要么左侧有序要么右侧有序
			if(arr[mid]>=arr[begin]) {//左侧有序
				begin=mid;
			}else {
				end=mid;
			}
		}
		return arr[end];
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值