近期代码题学习

算法题

long溢出

做网易笔试题,c(99,100)按照如下办法溢出:-90929067940350457
BigInteger 比long更大的整数

	public static long paizu(int n,int m,long k ) {
		if(n-1 ==0||m==0)
			return 1;
		long t =1;
		long sum = n+m-1;
//		m = Math.min(n-1, m);//C(m+n) n=C(m+n) m  取最小即可
		for(int i=0;i<m;i++) {
			t *= sum-i;
            t /= (i+1);
//			if(t>k)
//				break;
		}
		
		return t;
	}
}

可以优化:C(m+n) n=C(m+n) m 取最小即可 减少循环次数

	public static long paizu(int n,int m,long k ) {
		if(n-1 ==0||m==0)
			return 1;
		long t =1;
		long sum = n+m-1;
		m = Math.min(n-1, m);//C(m+n) n=C(m+n) m  取最小即可
		for(int i=0;i<m;i++) {
			t *= sum-i;
            t /= (i+1);
			if(t>k)
			break;
		}
		
		return t;
	}
}

java四舍五入并保留m位小数

//例子是输出六位小数的百分比,不用输出百分号
int wei =1000000;
double suoqiu = (double)((int)(((double)(k-1)/(double)n)*100*wei+0.5))/wei;
System.out.println(String.format("%.6f", suoqiu));
//如果是正常的小数
int wei =1000000;
double suoqiu = (double)((int)(((double)(k-1)/(double)n)*wei+0.5))/wei;
System.out.println(String.format("%.6f", suoqiu));//6是输出的小数的位数

合并两个有序数组

leetcode

import java.util.Arrays;

public class mergeSorted {
    public static int[] merge(int[] nums1, int m, int[] nums2, int n) {
    	int i=m-1;
    	int j =n-1;
    	int k = m+n-1;
    	while(i>=0&&j>=0) {
    		if(nums1[i]>=nums2[j]) {
    			nums1[k] = nums1[i];
    			i--;
    		}else {
    			nums1[k] = nums2[j];
    			j--;
    		}
    		k--;
    	}
    	while(j>=0) {
    		nums1[k--]=nums2[j--];
    	}
        return nums1;
    }
    public static void main(String[] args) {
    	int[] nums1 = new int[] {1,2,3,0,0,0};
    	int[] nums2= {2,5,6};
    	int[] a = merge(nums1, 3,nums2, 3);
    	//System.out.println(a.toString());//错误:打印的是地址值
    	System.out.println(Arrays.toString(a));
	}
}
值得注意:1.打印出数组的值:    	
//System.out.println(a.toString());//错误:打印的是地址值
    	System.out.println(Arrays.toString(a));
    	2.用的是nums1保留原有合并结果:
    	循环结束后,有可能i和j还大于等于0,若j大于等于0,那么需要继续循环,将nums2中的数字继续拷入nums1。若是i大于等于0,那么就不用管,因为混合数组本身就房子nums1中。
    	如果用nums3的话,循环结束i/j 有大于等于0 的都需要循环

DP动态规划

硬币1:

322.零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

import java.util.Arrays;
public class Coin1 {
    public static int coinChange(int[] coins, int amount) {
    	if(coins.length==0)
    		return -1;
    	int[] dp = new int[amount+1];
    	Arrays.fill(dp,1,dp.length,Integer.MAX_VALUE);
        for(int i=0;i<coins.length;i++) {
        	for(int j=coins[i];j<=amount;j++) {
        		if(dp[j-coins[i]] != Integer.MAX_VALUE) {
    				dp[j] = Math.min(dp[j], dp[j-coins[i]]+1);
    			}
        	}
        }
        if(dp[amount] != Integer.MAX_VALUE)
    		return dp[amount];
    	return -1;
        
    }
	public static void main(String[] args) {
		int[] coins1 = {1, 2, 5};
		int amount1=11;
		System.out.println(coinChange(coins1,amount1));
		int[] coins2 = {2};
		int amount2=3;
		System.out.println(coinChange(coins2,amount2));
	}

}

注意:1.初始化数组,边界dp[0]=0,其他为无穷大
2.for循环方向:越少重复的方向

硬币2:

518.零钱兑换2
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

public class Coin2 {
    public static int change(int amount, int[] coins) {
    	//输入0,[]:默认输出1
//    	if(coins.length==0)
//    		return 0;
    	int[] dp = new int[amount+1];
    	dp[0]=1;//d[0] 时为初始条件,表示0元可以用0元兑换1次,所以 d[0] = 1。
        for(int i=0;i<coins.length;i++) {
        	for(int j=coins[i];j<=amount;j++) {
        		dp[j] = dp[j]+dp[j-coins[i]];	
        	}
        }
        return dp[amount]; 
    }
	public static void main(String[] args) {
		int[] coins1 = {1, 2, 5};
		int amount1=5;
		System.out.println(change(amount1,coins1));
		int[] coins2 = {2};
		int amount2=3;
		System.out.println(change(amount2,coins2));
		int[] coins3 = {10};
		int amount3= 10;
		System.out.println(change(amount3,coins3));		
	}
}

注意:1.初始化数组,边界dp[0]=1,其他为0
2.for循环方向:越少重复的方向

进制转换

String str4 = Integer.toString(i,x) ;  ///10进制的数字i转换成x进制的字符串
int y4=Integer.valueOf("101",7);        //7进制转换成10进制
System.out.println(y4);
Integer.valueOf("str",x); ///可以为任意进制的字符串str转换成x进制的10进制数
System.out.println("其它的可能用到的函数:");
//static int parseInt(String s, int radix) //使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
int n = Integer.parseInt("776", 8) ; ///8进制转换成10进制
System.out.println(n);
///Integer.valueOf()返回一个“integer对象”和Integer.parseInt()返回一个“int值”的区别在于,返回值不同
///基本常识,其他的非10进制的数的保存,基本都是以字符串的形式
///例子:7进制到8进制的转换
String q = "6523" ; ///7进制的字符串
String b = Integer.toString(Integer.valueOf(q,7),8) ;///这样7进制就变成8进制了

华为+商汤笔试:七进制求和


/*
 * 两种做法:
 * 1.七进制转化为十进制,求和后在转化为七进制
 * 2.两个七进制数逐一相加,有进位进一
 */
import java.util.Arrays;

public class st {
	public static void main(String[] args) {
		
		System.out.println(sumSeven(361,512));
		System.out.println(sumSeven(15,12));
		System.out.println(jinzhi(361,512));
		System.out.println(jinzhi(15,12));
	}
	public static String sumSeven(int m,int n) {
        int[] a = fen(m);
        int[] b= fen(n);
        int i=0;
        int[] sum = new int[10];
        int flag =0;
        while(i<a.length || i<b.length) {
        	
        	sum[i] = a[i]+b[i]+flag;
        	flag =0;
        	if(sum[i]>=7) {
        		sum[i] =sum[i]-7;
        		flag =1;
        	}
        	i++;
        }
        if(flag==1) {
        	sum[i] =1;
        	i++;
        }
        sum = Arrays.copyOfRange(sum, 0, i);// 截断数组
        String a1 ="";
        for(int i1=sum.length-1;i1>=0;i1--) {
        	
        	a1 += sum[i1];
        }
        return a1;
	}
	public static int[] fen(int m) {
		int[] m1 = new int[10];//假设整数有这么多位数字
        int i = 0;
        if (m > 0) { // 正整数
            while (m > 0) {
                m1[i] = m % 10;
                m = m / 10;
                i++;
            }
        }
        int[] n = Arrays.copyOfRange(m1, 0, i);// 截断数组
        return n;
        
	}
	
	/*
	 * 进制转换
	 */
	public static String jinzhi(int m,int n) {
		//str = String.valueOf(int):int转string
		int a =Integer.valueOf(String.valueOf(m),7);//7进制转10进制
		int b =Integer.valueOf(String.valueOf(n),7);
		int c =a+b;
		String d = Integer.toString(c, 7);//10进制转7进制
		return d;
	}

}

字符串

在这里插入图片描述
面试的时候让手写代码
在这里插入图片描述
以上代码有几个错误:
1.定义的存储数组a 应该是字符串类型,不应该是int类型
2.for循环里面:i<a.length()是错误的,因为for循环里面还有i+1
3.大写字母的ASCII码比小写字母小32,不是+32
4.直接 a[i] =s[i+1]-32;i++;会导致此时的a[i]为空
5.返回值应该是字符串类型,因为自己不会写返回数组类型
总结:还是要快速大量的刷题;字符串掌握不好
自己私下敲了一下代码,用了 两种方法,一种侧重于引入数组,另一种侧重于字符串本身的操作。
引入数组

public class XiahuaxianDaxie {
	public static String transform(String s) {
		String [] a = new String[s.length()];
		int l=0;
		for(int i=0;i<s.length()-1;i++) {
			
			if(s.charAt(i) =='_') {
				a[i] ="";//char没有'';
				i++;
				a[i] =String.valueOf((char)(s.charAt(i)-32));
				
			}else
			{
				a[i] =String.valueOf(s.charAt(i));
			}
			l=i;
		}
		a[l+1] =String.valueOf(s.charAt(s.length()-1));
		
		StringBuilder str = new StringBuilder();
		for (String s1 : a) {
		    str.append(s1);
		}
		return str.toString(); // 012345
	}
	public static void main(String[] args) {
		System.out.println(transform("my_str_name"));
		System.out.println(transform("my_str_my_list_item"));
		System.out.println(transform("my_str my_list_item"));
		System.out.println(transform("usenr_name_uu_u_"));
		
	}

}

其中,字符串数组转字符串的方法有下面两种:
引入StringBuilder/StringBuffer

		StringBuilder str = new StringBuilder();
		for (String s1 : a) {
		    str.append(s1);
		}
		return str.toString(); 

直接字符串操作:

		String str1 =" ";
		for (String s1 : a) {
		    str1 += s1;
		}
		return str1;

只有String类型的有"",char没有空’’.
字符串操作:

public class XiahuaxianDaxie2 {
	   //首字母大写
    public static String captureName(String name) {
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
       return  name;
      
    }
//将字符串name 转化为首字母大写。但是这种效率并不高,我之前看过一个牛人的写的方法核心代码,是这样的

    //首字母大写
    public static String captureName1(String name) {
        char[] cs=name.toCharArray();
        cs[0]-=32;
        return String.valueOf(cs);
        
    }
	public static String transform(String s) {
		String newString ="";
		if(s.indexOf("_")!=-1 && s.indexOf("_") !=s.length()-1) {
			while(s.indexOf("_")!=-1 && s.indexOf("_") !=s.length()-1) {
				int first = s.indexOf("_");
				newString = newString + s.substring(0, first);
				s =captureName(s.substring(first+1));
			}
			newString += s;
			return newString;
		}
		else {
			return s;
		}
	}
	public static void main(String[] args) {
		System.out.println(transform("my_str_name"));
		System.out.println(transform("my_str_my_list_item"));
		System.out.println(transform("my_str my_list_item"));
		System.out.println(transform("usenr_name_uu_u_"));
		
	}

}

其中涉及字符串的操作在java基础知识讲解。

单例模式

参考:
代码解释
6种
5种
7种
为什么使用 枚举类

深度优先搜索和广度优先搜索

小灰
孙正豪

求二叉树的深度或者广度

牛客
三种方法
leetcode

二叉树的层序遍历(bfs)

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;


public class levelOrder {
	public static List<List<Integer>> level(TreeNode root) {
		List<List<Integer>> res = new ArrayList<>();
		if(root==null)
			return res;
		Queue<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		while(!queue.isEmpty()) {
			int count = queue.size();
			List<Integer> list = new ArrayList<Integer>();
			while(count>0) {
//				System.out.println("count");
//				System.out.println(count);
				TreeNode node = queue.poll();
	            list.add(node.val);
//	            System.out.println(node.val);
	            if(node.left != null)
	                queue.add(node.left);
	            if(node.right != null)
	                queue.add(node.right);
	            count--;
//	            System.out.println(count);
			}
			res.add(list);
		}
	        return res;
	    }
	 public static void main(String[] args) {
		 TreeNode aBiTree = new TreeNode (1);
	        
	        aBiTree.left=new TreeNode(2);
	        aBiTree.right=new TreeNode(3);
	        aBiTree.left.right=new TreeNode(4);
	        System.out.println(level(aBiTree));

}
}
class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode(int x) { val = x; }
 }

LinkedHashMap实现LRU

//整型
import java.util.LinkedHashMap;
import java.util.Map;

public class LRU {
    LinkedHashMap<Integer, Integer> hashMap;

    public LRU(int capacity) {


        hashMap = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {

                return size() > capacity;
            }
        };
    }

    public int get(int key) {
        return hashMap.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        hashMap.put(key, value);
    }
    public void print() {
		for (Map.Entry entry : hashMap.entrySet()) {
			System.out.print(entry.getValue() + "--");
		}
		System.out.println();
	}
    public static void main(String[] args) {
    	LRU cache = new LRU( 2 /* 缓存容量 */ );

    	cache.put(1, 1);
    	cache.put(2, 2);
    	cache.print();
    	System.out.println(cache.get(1));       // 返回  1
    	cache.print();
    	cache.put(3, 3);    // 该操作会使得密钥 2 作废
    	cache.print();
    	System.out.println(cache.get(2)); // 返回 -1 (未找到)
    	cache.put(4, 4);    // 该操作会使得密钥 1 作废
    	cache.get(1);       // 返回 -1 (未找到)
    	cache.get(3);       // 返回  3
    	cache.get(4);       // 返回  4
    }
}

//泛型
import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache<K,V> {
	LinkedHashMap <K,V> hashmap;
	public LRUCache(int capacity) {
		hashmap = new LinkedHashMap <K,V>(capacity, 0.75f, true) {
			protected boolean removeEldestEntry(Map.Entry<K,V> eldest){
			return size()> capacity;
			}
		};
	}
	public V get(K key) {
		return hashmap.get(key);
	}
	public void put(K key,V value) {
		hashmap.put(key,value);
	}
	public void print() {
		for (Map.Entry<K, V> entry : hashmap.entrySet()) {
			System.out.print(entry.getValue() + "--");
		}
		System.out.println();
	}
	public static void main(String[] args) {
		LRUCache<String, Integer> LRUMap = new LRUCache<>(3);
		LRUMap.put("key1", 1);
		LRUMap.put("key2", 2);
		LRUMap.put("key3", 3);
		LRUMap.print();
		System.out.println();
		LRUMap.put("key4", 4);
		LRUMap.print();
		System.out.println(LRUMap.get("key4"));
		System.out.println(LRUMap.get("key5"));
	}
}

微信拼手气红包

二倍均值法:

结论:剩余红包金额为M,剩余人数为N,那么有如下公式:
每次抢到的金额 = 随机区间 (0, M / N X 2)
这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。
举例子:
假设有10个人,红包总额100元。
100/10X2 = 20, 所以第一个人的随机范围是(0,20 ),平均可以抢到10元。
假设第一个人随机到10元,那么剩余金额是100-10 = 90 元。
90/9X2 = 20, 所以第二个人的随机范围同样是(0,20 ),平均可以抢到10元。
…保证每次机会均等。

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
//@param totalAmount 总金额(以分为单位)
//二倍均值法
public class hongbao {
	public static List<Integer> divideRedPackage(Integer totalAmount, Integer totalPeopleNum){
		List<Integer> amountList = new ArrayList<Integer>();
		Integer restAmount = totalAmount;
		Integer restPeopleNum = totalPeopleNum;
		Random random = new Random();
		for(int i=0;i<totalPeopleNum-1;i++) {
			//随机范围:[1,剩余人均金额的2倍-1]分:保证平均值为剩余人均金额
			int amount =random.nextInt(restAmount/restPeopleNum*2-2)+1;
			restAmount -= amount;
			restPeopleNum --;
			amountList.add(amount);
		}
		amountList.add(restAmount);//最后一个人剩余的钱
		return amountList;
	}
	public static void main(String[] args) {
		List<Integer> amountList = divideRedPackage(1000,10);
		//增强for循环 foreach
		for(Integer amount:amountList) {
			System.out.println("抢到金额:"+new BigDecimal(amount).divide(new BigDecimal(100)));
			//System.out.println("抢到金额:"+amount/100);//结果取整 没有小数点
		}
	}

}

线段切分法

二倍均值法缺点:除了最后一次,任何一次的金额都小于人均金额的两倍,并不是任意的均等。
在这里插入图片描述
如何确定每一条子线段的长度呢?由“切割点”来决定。当N个人一起抢红包的时候,就需要确定N-1个切割点。
因此,当N个人一起抢总金额为M的红包时,我们需要做N-1次随机运算,以此确定N-1个切割点。随机的范围区间是(1, M)。
当所有切割点确定以后,子线段的长度也随之确定。这样每个人来抢红包的时候,只需要顺次领取与子线段长度等价的红包金额即可。
这就是线段切割法的思路。在这里需要注意以下两点:
1.当随机切割点出现重复,如何处理。
2.如何尽可能降低时间复杂度和空间复杂度。

import java.math.BigDecimal;
import java.util.*;
//这个方法也更方便抑制红包金额MAX情况,如果金额index-start>MAX,就直接把红包设为最大值MAX,
//然后随机点重置为start+MAX,保证所有红包金额相加等于总金额。
public class hongbaoxianduanqiege {
	public static List<Integer>  divideRedPackage(int allMoney, int peopleCount,int MAX) {
		//人数比钱数多则直接返回错误
        if(peopleCount<1||allMoney<peopleCount){
            System.out.println("钱数人数设置错误!");
            return null;
        }
        List<Integer> indexList = new ArrayList<>();
        List<Integer> amountList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < peopleCount - 1; i++) {
            int index;
            do{
                index = random.nextInt(allMoney - 2) + 1;
            }while (indexList.contains(index));//解决碰撞
            indexList.add(index);
        }
        Collections.sort(indexList);
        int start = 0;
        for (Integer index:indexList) {
                //解决最大红包值
            if(index-start>MAX){
                amountList.add(MAX);
                start=start+MAX;
            }else{
                amountList.add(index-start);
                start = index;
            }
        }
	
    amountList.add(allMoney-start);
    return amountList;
}
    public static void main(String args[]){
        Scanner in=new Scanner(System.in);
        int n=Integer.parseInt(in.nextLine());
        int pnum=Integer.parseInt(in.nextLine());
        int money=n*100;int max=n*90;
        List<Integer> amountList = divideRedPackage(money, pnum,max);
        if(amountList!=null){
            for (Integer amount : amountList) {
                System.out.println("抢到金额:" + new BigDecimal(amount).divide(new BigDecimal(100)));
            }
        }}

}

在这里插入图片描述
在这里插入图片描述

五亿大整数,怎么排

外部排序,bitmap

BItmap 大数据的应用

大数据bitmap

买卖股票


dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
              max(   选择 rest  ,           选择 sell      )
 
解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有;
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。
 
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
              max(   选择 rest  ,           选择 buy         )
 
解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest,所以我今天还持有着股票;
要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。
dp[-1][k][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0 。
dp[-1][k][1] = -infinity
解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0 。
dp[i][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的,用负无穷表示这种不可能。

状态转移方程:

base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -infinity
 
状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值