有3n+1个数字,其中3n个中是重复的,只有1个是不重复的,怎么找出来
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {1,2,3,4,6,7,9,6,8};
System.out.println(findOneNum(array));
}
public static int findOneNum(int[] array) {
// 数组长度
int length = array.length;
// 要找的数
int num;
// 如果是奇数的情况
if(length & 1 != 0){
int temp = array[0];
int i = 1;
while (i < length){
temp = temp ^ array[i];
i++;
}
num = temp;
}else{
for(int i=0;i<length;i++){
//
}
}
return temp;
}
}
有1亿个数字,其中有2个是重复的,快速找到它,时间和空间要最优
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {1,2,3,4,6,7,9,6,8};
System.out.println(bigDataFindOneNum(array));
}
/**
* 有1亿个数字,其中有2个是重复的,快速找到它,时间和空间要最优
* 用位图法解决
* @param array 一亿个数据的数据源
* @return 返回重复的数字或不存在返回-1
*/
public static int bigDataFindOneNum(int[] array) {
// 最大数 + 1
int max = 100000001;
// 初始化位图
BitSet bitSet = new BitSet(max);
int i = 0;
while (i < array.length){
int value = array[i];
//判断该数是否存在bitSet里
if (bitSet.get(value)){
// 返回重复的数
return value;
}else {
bitSet.set(value, true);
}
i++;
}
// 不存在返回-1
return -1;
}
}
现在有50亿个int类型的正整数,要从中找出重复的数并返回
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
Set<Integer> t2 = bigNumberFind(array);
System.out.println(t2);
}
/**
* 题目描述: 现在有五十亿个int类型的正整数,要从中找出重复的数并返回
* 分析: 判断50亿个数有哪些是重复和判断一个数置否存在50亿个数当中是否存在,其实是一样的。
* 我们采用bitmap算法来做。不过这里50亿个数,别人肯定是以文件流的形式给你的。
* 这样我们为了方便,我们就假设这些数是以存在int型数组的形式给我们的。
* @param array
* @return
*/
public static Set<Integer> bigNumberFind(int[] array){
int j = 0;
//用来把重复的数返回,存在Set里,这样避免返回重复的数
Set<Integer> output = new HashSet<>();
BitSet bitSet = new BitSet(Integer.MAX_VALUE);
int i = 0;
while (i < array.length){
int value = array[i];
//判断该数是否存在bitSet里
if (bitSet.get(value)){
output.add(value);
}else {
bitSet.set(value, true);
}
i++;
}
return output;
}
}
二分查找法(折半查找法)
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
System.out.println(binarySearch(array, 7));
}
/**
* 二分查找法
* @param array
* @param target
* @return
*/
public static int binarySearch(int []array,int target){
//查找范围起点
int start=0;
//查找范围终点
int end=array.length-1;
//查找范围中位数
int mid;
while(start<=end){
//mid=(start+end)/2 有可能溢出
mid=start+(end-start)/2;
if(array[mid]==target){
return mid;
} else if(array[mid]<target){
start=mid+1;
}else{
end=mid-1;
}
}
return -1;
}
}
小偷来到了一个神秘的王宫,突然眼前一亮,发现5个宝贝,每个宝贝的价值都不一样,且重量也不一样,但是小偷的背包携带重量有限,所以他不得不在宝贝中做出选择,才能使偷到的财富最大,请你帮助小偷计算一下(华为编程题)
/**
* 输入描述:
* 宝贝价值:6,3,5,4,6
* 宝贝重量:2,2,6,5,4
* 小偷背包容量:10
* 输出描述:
* 偷到宝贝的总价值:15
* 示例1
* 输入
* 6,3,5,4,6
* 2,2,6,5,4
* 10
* 输出
* 15
*/
public class Demo {
public static void main(String[] args) {
// 打开输入
Scanner sc = new Scanner(System.in);
// 宝贝价值字符串
String str1 = sc.next();
// 宝贝重量字符串
String str2 = sc.next();
// 背包容量字符串
String str3 = sc.next();
// 宝贝价值数组
int[] value = transform(str1);
// 宝贝重量数组
int[] weight = transform(str2);
// 背包容量数组
int[] bag = transform(str3);
// 关闭输入
sc.close();
// 获取第一个背包容量
int m = bag[0];
int n = value.length;
int w[] = weight;
int p[] = value;
int c[][] = backPackSolution(m, n, w, p);
System.out.println(c[c.length-1][c[0].length-1]);
}
public static int[][] backPackSolution(int m, int n, int[] w, int[] p) {
//c[i][v]表示前i件物品恰放入一个重量为m的背包可以获得的最大价值
int c[][] = new int[n + 1][m + 1];
for (int i = 0; i < n + 1; i++){
c[i][0] = 0;
}
for (int j = 0; j < m + 1; j++){
c[0][j] = 0;
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
//当物品为i件重量为j时,如果第i件的重量(w[i-1])小于重量j时,c[i][j]为下列两种情况之一:
//(1)物品i不放入背包中,所以c[i][j]为c[i-1][j]的值
//(2)物品i放入背包中,则背包剩余重量为j-w[i-1],所以c[i][j]为c[i-1][j-w[i-1]]的值加上当前物品i的价值
if (w[i - 1] <= j) {
if (c[i - 1][j] < (c[i - 1][j - w[i - 1]] + p[i - 1])){
c[i][j] = c[i - 1][j - w[i - 1]] + p[i - 1];
}else{
c[i][j] = c[i - 1][j];
}
} else{
c[i][j] = c[i - 1][j];
}
}
}
return c;
}
/**
* 把字符串转换为数组
*/
private static int[] transform(String str1) {
String[] split = str1.split(":");
String[] split2 = split[split.length-1].split(",");
int[] value=new int[split2.length ];
for (int i = 0; i < value.length; i++) {
value[i]=Integer.parseInt(split2[i]);
}
return value;
}
}
给定一个正数数组arr(即数组元素全是正数),找出该数组中,两个元素相减的最大值,其中被减数的下标不小于减数的下标。即求出:maxValue = max{arr[j]-arr[i] and j >= i}
public class Demo {
public static int maxValueSub(int[] array){
// 最大值
int max = 0; // 当 i=j=0 时
// 最小减数下标
int i = 0;
// 差
int sub;
for (int j = 1; j < array.length; j++) {
sub = array[j] - array[i];
if (sub>max){
// 判断差是否大于最大值
max = sub;
}else if (sub <0){
// 记录最小减数下标
i= j;
}
}
return max;
}
public static void main(String[] args) {
int [] array = {1,2,4,6,7};
System.out.println(maxValueSub(array));
}
}
实现一个函数把两个有序的int数组结合成新的有序数组
public class Demo {
public static int[] mergeTwoSortedArrays(int[] array1, int[] array2){
int l1 = array1.length;
int l2 = array2.length;
// 定义一个数组
int [] result = new int [l1+l2];
int i=0,j=0,k=0; //分别代表数组array1 ,array2 , result 的索引
// 同时遍历 array1、array2 数组
while (i<l1 && j<l2){
if (array1[i]<array2[j]){
// 如果 array1[i]小于array2[j],则先把array1[i]的放入 result数组中
result[k++] = array1[i++];
}else {
result[k++] = array2[j++];
}
}
// 判断 array1 是否遍历完
while (i<l1){
result[k++] = array1[i++];
}
// 判断 array2 是否遍历完
while (j<l2){
result[k++] = array2[j++];
}
return result;
}
public static void main(String[] args) {
int [] array = {1,2,4,6,7};
System.out.println(Arrays.toString(mergeTwoSortedArrays(array,array)));
}
}
求一个数组中不存在的最小正整数
public class Demo {
// 采用标记法可以简单实现
public static int firstMissingPositive(int[] array){
// 如果数组为空,则返回1
if (array == null || array.length<=0)
{
return 1;
}
int n = array.length;
// 定义一个长度为array.length 的临时数组
int[] tempArray = new int[n];
for (int i = 0; i < array.length; i++) {
// 如果 array[i] 在 1 -- n 的范围,则在临时数组 array[i]-1的位置标记1
if (array[i]>=1 && array[i] <= n){
tempArray[array[i]-1] = 1;
}
}
// 遍历tempArray数组判断是否标记为1,如果不是1,则返回下表对应的正整数 i+1
for (int i = 0; i < tempArray.length; i++) {
if (tempArray[i] != 1){
return i+1;
}
}
//如果rempArray对应位置都标记为1,则 n+1 就是不存在的正整数
return n+1;
}
public static void main(String[] args) {
int [] array = {3,4,2,5};
System.out.println(firstMissingPositive(array));
}
}
求两个大整数的和
public class Demo {
public static String bigNumberSum(String bigNumberA, String bigNumberB) {
//1.把两个大整数用数组逆序存储,数组长度等于较大整数位数+1
int maxLength = bigNumberA.length() > bigNumberA.length() ? bigNumberA.length() : bigNumberB.length();
int[] arrayA = new int[maxLength + 1];
int[] arrayB = new int[maxLength + 1];
for (int i = 0; i < bigNumberA.length(); i++) {
arrayA[i] = bigNumberA.charAt(bigNumberA.length() -1-i) - '0'; //“ - '0'”是将String型转化为int型
}
for (int i = 0; i < bigNumberB.length(); i++) {
arrayB[i] = bigNumberB.charAt(bigNumberB.length() -1-i) - '0'; //“ - '0'”是将String型转化为int型
}
//2.构建result数组,数组长度等于较大整数位数+1
int[] result = new int[maxLength + 1];
//3.遍历数组,按位相加
for (int i = 0; i < result.length; i++) {
int temp = result[i];
temp += arrayA[i];
temp += arrayB[i];
//判断是否进位
if (temp > 10){
temp = temp - 10;
result[i+1] = 1;
}
result[i] = temp;
}
//4.把result数组再次逆序并转成String
StringBuilder sb = new StringBuilder();
//是否找到大整数的最高有效位
boolean findFirst = false;
for (int i = 0; i < result.length; i++) {
if (! findFirst){
if (result[i] == 0){
continue;
}
findFirst = true;
}
sb.append(result[i]);
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(bigNumberSum("426709752318", "95481253129"));
}
}
求两个大整数的积
public class Demo {
public static int isPowerOf2(int num) {
return (num&num-1) == 0;
}
public static void main(String[] args) {
System.out.println(isPowerOf2(1200));
}
}
如何判断一个数是否为2的整数次幂
// 最快的方法就是采用位与运算
// 如果一个整数是2的整数次幂,那么当它转化成二进制时,只有最高位是1,其他位都是0!
// 如果2的整数次幂一旦减1,它的二进制数字就全部变成了1!
// 而0和1按位与运算的结果是0,所以凡是2的整数次幂和它本身减1的结果进行与运算,结果都必定是0。
// 反之,如果一个整数不是2的整数次幂,结果一定不是0!
// 最后,对于一个整数n,只需要计算n&(n-1)的结果是不是0。这个方法的时间复杂度只有O(1)
public class Demo {
public static int isPowerOf2(int num) {
return num > 0 && (num&num-1) == 0;
}
public static void main(String[] args) {
System.out.println(isPowerOf2(1200));
}
}
如何判断一个数是否为4的整数次幂
// 4的幂首先是2的幂,因为4^n = (2^2)^n;
// 先判断是否为2的幂,同样利用n & (n - 1);
// 不同是,4的幂的二进制表示中,1全奇数位上。
// 所以进一步判断其与0x55555555按位与的结果,0x55555555是十六进制表示,
// 换成二进制表示,可以发现,其奇数位上全是1,那么相与结果为true,则是4的幂,否则不是
public class Demo {
public static int isPowerOf4(int num) {
return num > 0 && ((num&num-1) == 0) && ((num & 0x55555555) != 0);
}
public static void main(String[] args) {
System.out.println(isPowerOf4(1200));
}
}
给你一组整型数据,这些数据中,其中有一个数只出现了一次,其他的数都出现了两次,让你来找出一个数。
// 高效的解决方法就是采用位异或运算方法
public class Demo {
public static int mergeSort(int[] array) {
int temp = array[0];
for (int i = 1; i < array.length; i++) {
temp = temp ^ array[i];
}
return temp;
}
public static void main(String[] args) {
int[] array2 = {1,2,3,4,6,1,3,4,6};
System.out.println(findNumber(array2));
}
}
用多线程统计1到1000000之间有多少素数
code
给一个数组,归并排序
// 给数组进行归并排序
public class Demo {
public static void mergeSort(int[] array, int start, int end) {
if (start < end){
//折半成两个小集合,分别进行递归
int mid = (start + end)/2;
mergeSort(array, start, mid);
mergeSort(array, mid+1, end);
// 把两个有序小集合,归并成一个大集合
merge(array, start, mid, end);
}
}
private static void merge(int[] array, int start, int mid, int end){
//开辟额外大集合,设置指针
int[] tempArray = new int[end-start+1];
int p1 = start; //左指针
int p2 = mid + 1; //右指针
int p = 0; //数组临时指针
//比较两个小集合的元素,依次放入大集合
while (p1 <= mid && p2 <= end){
if (array[p1] <= array[p2]){
tempArray[p++] = array[p1++];
}else {
tempArray[p++] = array[p2++];
}
}
//左侧小集合还有剩余,依次放入大集合尾部
while (p1<=mid){
tempArray[p++] = array[p1++];
}
//右侧小集合还有剩余,依次放入大集合尾部
while (p2<=end){
tempArray[p++] = array[p2++];
}
//把大集合的元素复制回原数组
for (int i=0; i<tempArray.length; i++){
array[i+start] = tempArray[i];
}
}
public static void main(String[] args) {
int[] array = {5, 8, 6, 3, 9, 2, 1, 7};
mergeSort(array,0,array.length-1);
System.out.println(Arrays.toString(array));
}
}
二叉树遍历
// 通过Map.entrySet遍历key和value
public class Demo {
/**
* 构建二叉树(先建左树在建右树的方法)
* @param inputList 输入序列
* @return
*/
public static TreeNode createBinaryTree(LinkedList<Integer> inputList){
TreeNode node = null;
if (inputList == null || inputList.isEmpty()){
return null;
}
Integer data = inputList.removeFirst();
if (data != null){
node = new TreeNode(data);
node.leftChild = createBinaryTree(inputList);
node.rightChild = createBinaryTree(inputList);
}
return node;
}
/**
* 二叉树节点
*/
private static class TreeNode {
int data;
TreeNode leftChild;
TreeNode rightChild;
TreeNode(int data){
this.data = data;
}
}
/**
* 二叉树遍历(递归)
* @param node 二叉树节点
*/
public static void preOrderTraveral(TreeNode node){
if (node == null){
return;
}
System.out.println(node.data); // 前序遍历打开 根-> 左-> 右
preOrderTraveral(node.leftChild);
//System.out.println(node.data); // 中序遍历打开 左-> 根-> 右
preOrderTraveral(node.rightChild);
//System.out.println(node.data); // 后序遍历打开 左-> 右-> 根
}
/**
* 二叉树非递归遍历
* @param root 二叉树根节点
*/
public static void preOrderTraveralWithStack(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
TreeNode treeNode = root;
while (treeNode != null || !stack.isEmpty()){ //节点不为空,结点入栈,并且指向下一个左孩子
while (treeNode != null){
System.out.println(treeNode.data); // 前序遍历打开
stack.push(treeNode);
treeNode = treeNode.leftChild;
}
if (!stack.isEmpty()){
treeNode = stack.pop();
System.out.println(treeNode.data); // 中序遍历打开
treeNode = treeNode.rightChild;
}
}
}
/**
* 二叉树非递归后序遍历
* @param root 二叉树根节点
*/
public static void postOrderTraveralWithStack(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
TreeNode treeNode = root;
TreeNode lastVisit = null; // 标记每次遍历最后一次访问的节点
while (treeNode != null || !stack.isEmpty()){ // 节点不为空,结点入栈,并且指向下一个左孩子
while (treeNode != null){
stack.push(treeNode);
treeNode = treeNode.leftChild;
}
// 栈不为空
if (!stack.isEmpty()){
// 出栈
treeNode = stack.pop();
if(treeNode.rightChild == null || treeNode.rightChild == lastVisit) {
System.out.println(treeNode.data);
lastVisit = treeNode;
treeNode = null;
}else{
stack.push(treeNode);
treeNode = treeNode.rightChild;
}
}
}
}
/**
* 二叉树层序遍历
* @param root 二叉树根节点
*/
public static void levelOrderTraversal(TreeNode root){
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
TreeNode node = queue.poll();
System.out.println(node.data);
if (node.leftChild != null){
queue.offer(node.leftChild);
}
if (node.rightChild != null){
queue.offer(node.rightChild);
}
}
}
public static void main(String[] args) {
LinkedList<Integer> inputList = new LinkedList<>(Arrays.asList(new Integer[]{3,2,9,null,null,10,null,null,8,null,4}));
TreeNode treeNode = createBinaryTree(inputList);
System.out.println(" 二叉树递归前序遍历: ");
preOrderTraveral(treeNode);
System.out.println(" 二叉树非递归前序遍历: ");
preOrderTraveralWithStack(treeNode);
System.out.println(" 二叉树层序遍历: ");
levelOrderTraversal(treeNode);
}
}
如何判断一个字符串中某个字符出现的次数?
// 面试官想要的答案不是for循环,而是字符串替换计算法
public class Demo {
public static int cuntCharNum(String str, String searchChar) {
// 字符串原始长度
int origialLength = str.length();
// 把要查找的字符串替换为空字符串
str = str.replace(searchChar, "");
// 替换后字符串的长度
int newLength = str.length();
// 某个字符出现的次数 = 旧字符串的长度 - 替换后字符串的长度
retrun origialLength - newLength;
}
public static void main(String[] args) {
String str = "ABC123ABC";
String searchChar = "B";
System.out.println("字符" + searchChar + "出现的次数为:" + cuntCharNum(str,searchChar));
}
}
如何反转一个字符串?
// 面试官问这个问题想要的答案是可以利用数据结构中的栈,因为栈的特点是先入后出,即可实现字符串的反转
public class Demo {
public static int myReverse(String str) {
// 把字符串转为字符数组
char[] charArray = str.toCharArray();
// 定义一个栈
Stack<Character> stack = new Stack<>();
// 定义一个新的字符串
StringBuilder newStr = new StringBuilder();
// 把字符数组入栈
for (char item : charArray) {
stack.push(item);
}
// 出栈,实现字符串反转
for (int i = 0; i < charArray.length; i++) {
// 出栈的字符添加到新的字符串前面
newStr.append(stack.pop());
}
return newStr;
}
public static void main(String[] args) {
String str = "ABC123ABC";
System.out.println("反转前:" + str);
System.out.println("反转后:" + myReverse(str).toString());
}
}
给定一个字符串A,字符串由所有数字和字母组成,求这个字符串里面所出现的数字和字母出现次数?
// 如 input: A="aaaBBBaaA23"...output:{A:1,B:3,a:5,2:1,3:1}
public class Demo {
public static int charCount(String str) {
StringBuilder cuntStr = new StringBuilder();
while (str.length()>0){
// 字符串的长度
int strLength = str.length();
// 获取字符串的第一个字符
char at = str.charAt(0);
// 字符串里的 at 字符替换为空字符串""
str = str.replace(""+ at +"", "");
// 计算字符出现的次数
int cunt = strLength - str.length();
// 添加到返回的字符串里面
cuntStr.append(","+ at +":" + cunt);
}
String result = "{" + cuntStr.substring(1) + "}";
return result;
}
public static void main(String[] args) {
System.out.println(charCount("aaaBBBaaA23"));
}
}
高效遍历 HashMap
// 通过Map.entrySet遍历key和value
public class Demo {
public static void hforMap(Map<String,String> map) {
for(Map.Entry<String, String> entry: map.entrySet())
{
System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue());
}
}
public static void main(String[] args) {
Map<String,String> map=new HashMap<String,String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
map.put("4", "value4");
hforMap(map);
}
}
发红包算法(二倍均值法、线段切割法)
// 金额参数以分为单位
public class Demo {
/**
* 二倍均值法
* @param totalAmount 红包金额
* @param totalPeopleNum 红包个数
* @return
*/
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,剩余人均金额的两倍),左闭右开
int amount = random.nextInt(restAmount / restPeopleNum * 2 - 1) + 1;
// 计算剩余红包金额
restAmount -= amount;
// 计算剩余红包个数
restPeopleNum--;
// 红包金额放入红包发放数组
amountList.add(amount);
}
amountList.add(restAmount);
return amountList;
}
/**
* 线段切割法
* @param totalAmount 红包金额
* @param totalPeopleNum 红包个数
* @return
*/
public static List<Integer> lineCuttingRedPackage(Integer totalAmount, Integer totalPeopleNum){
// 红包发放数组
List<Integer> amountList = new ArrayList<>();
// 切节点数组
List<Integer> list = new ArrayList<>();
// 随机数初始化
Random random = new Random();
while (list.size()<=totalPeopleNum-1) {
int i=random.nextInt(totalAmount)+1;//最低1分钱
if(list.indexOf(i)<0){//非重复切割添加到集合
list.add(i);
}
}
// 切点排序:由小到大
Collections.sort(list);
// 上一个切点值
int flag = 0;
// 累计切断的长度
int sumAmount = 0;
// 开始切
for (int i=0;i<list.size();i++) {
// 当前红包金额 = 当前切点值 - 上一个切点值
int amount=list.get(i)-flag;
flag = list.get(i);
sumAmount += amount;
amountList.add(amount);
}
// 最后一个红包
amountList.add(totalAmount-sumAmount);
return amountList;
}
public static void main(String[] args) {
List<Integer> amountList = divideRedPackage(5000, 30);
for (Integer amount : amountList) {
// 由分转换为元
BigDecimal decimal = new BigDecimal(amount).divide(new BigDecimal(100));
System.out.println("抢到金额:" + decimal);
}
List<Integer> list = lineCuttingRedPackage(5000, 30);
for (Integer amount : list) {
// 由分转换为元
BigDecimal decimal = new BigDecimal(amount).divide(new BigDecimal(100));
System.out.println("抢到金额:" + decimal);
}
}
}
动态规划解决扔鸡蛋问题
// 换句话说,有M层楼 / N个鸡蛋,要找到鸡蛋摔不碎的临界点,需要尝试几次?
public class Demo {
public static int getMinSteps(int eggNum, int floorNum) {
if (eggNum < 1 || floorNum < 1){
return 0;
}
//上一层备忘录,存储鸡蛋数量-1的floorNum层楼条件下的最优化尝试次数
int [] preCache = new int[eggNum+1];
//当前备忘录,存储当前鸡蛋数量的floorNum层楼条件下的最优化尝试次数
int [] currentCache = new int[floorNum+1];
//把备忘录每个元素初始化成最大的尝试次数
for (int i = 0; i <= floorNum; i++) {
currentCache[i] = i;
}
for (int n = 2; n <= eggNum; n++) {
// 当前备忘录拷贝给上一次备忘录,并重新初始化当前备忘录
preCache = currentCache.clone();
for (int i = 1; i <= floorNum; i++) {
currentCache[i] = i;
}
for (int m = 1; m <= floorNum; m++) {
for (int k = 1; k < m; k++) {
// 扔鸡蛋的楼层从1到m枚举一遍,如果当前算出的尝试次数小于上一次算出的尝试次数,则取代上一次的尝试次数。
// 这里可以打印k的值,从而知道第一个鸡蛋是从第几次扔的。
currentCache[m] =Math.min(currentCache[m], 1+Math.max(preCache[k-1],currentCache[m-k]));
}
}
}
return currentCache[floorNum];
}
public static void main(String[] args) {
System.out.println(getMinSteps(1,600));
}
}
去掉一个数,如何让剩余的数乘积最大
public class Demo {
/**
* 去掉一个数,如何让剩余的数乘积最大
* @param array
*/
public static int findRemovedIndex(int[] array) {
// 1.统计负元素的个数
int negativeCount = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] < 0){
negativeCount ++;
}
}
// 2.根据不同情况,选择要删除的元素
int tempIndex = 0;
if ((negativeCount&1) == 1){
//情况A:负数个数是奇数, 则删除最大负数
for (int i = 1; i < array.length; i++) {
if (array[i] <0){
if (array[tempIndex] >= 0 || array[i] > array[tempIndex]){
tempIndex = i;
}
}
}
return tempIndex;
}else {
//情况B:负数个数是偶数
if (negativeCount == array.length){
//子情况:所有元素都是负数,这删除最小的负数
for (int i = 1; i < array.length; i++) {
if (array[i] < array[tempIndex]){
tempIndex = i;
}
}
return tempIndex;
}
for (int i = 1; i < array.length; i++) {
if (array[i]>=0){
if (array[tempIndex]<0 || array[i]<array[tempIndex]){
tempIndex = i;
}
}
}
return tempIndex;
}
}
public static void main(String[] args) {
int [] array1 = {-4,3,-5,-7,-21,9,-1,5,6};
int index = findRemovedIndex( array1);
System.out.println("删除元素下标:"+array1[index]);
int []array2={4,3,5,-7,-21,9,-1,-5,6,0};
index =findRemovedIndex(array2);
System.out.println("删除元素下标:"+array2[index]);
int[]array3={-4,-3,-5,-7,-21,-9,-1,-8};
index=findRemovedIndex(array3);
System.out.println("删除元素下标:"+array3[index]);
}
}
选择排序
public class Demo {
/**
* 选择排序(每次只找出一个最小值或最大值)是个不稳定排序
* @param array
* @return
*/
public static void selectionSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
// 每次只找出一个最小的值进行交换位置
for (int j = i+1; j < array.length; j++) {
// 初始最小值下标索引
int minIndex = i;
if (array[i] > array[j]){
minIndex = j;
}
// 如果最小值下标索引与初始值不同,则需要进行数据交换位置
if (minIndex != i){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,6,1,3,4,6};
selectionSort(array);
System.out.println(Arrays.toString(array));
}
}
插入排序
public class Demo {
public static void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int insertValue = array[i];
int j = i -1;
// 从右向左比较元素的同时,进行元素复制
for (; j>0 &&array[j]>insertValue; j--) {
array[j+1] = array[j];
}
// insertValue的值插入适当的位置
array[j+1] = insertValue;
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,6,1,3,4,6};
insertSort(array);
System.out.println(Arrays.toString(array2));
}
}
冒泡排序
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {4,4,6,5,3,2,8,1,7,5,6,0,10};
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
public static void bubbleSort(int[] array) {
//临时变量
int temp = 0;
// 记录最后一次交换的位置
int lastExchangIndex = 0;
// 无序数列的边界,每次比较只需要比到这里为止
int sortBorder = array.length - 1;
for (int i = 0; i < array.length; i++) {
// 有序标记,每一轮的初始是true
boolean isSorted = true;
for (int j = 0; j < sortBorder; j++) {
if (array[j] > array[j+1]){
// 从小到大排序
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
// 有元素交换,所以不是有序,标记变为false
isSorted = false;
// 把无序数列的边界更新为最后一次交换元素的位置
lastExchangIndex = j;
}
}
sortBorder = lastExchangIndex;
if (isSorted){
break;
}
}
}
}
快速排序
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
quickSort(array, 0, array.length-1);
System.out.println(Arrays.toString(array));
}
/**
* 快速排序
* @param array
* @param L 指向数组第一个元素
* @param R 指向数组最后一个元素
*/
public static void quickSort(int[] array, int L, int R) {
// 递归结束条件 L 大于等于 R 的时候
if (L >= R){
return;
}
int i = L;
int j = R;
// 支点元素
int pivot = array[(L + R)/2];
// 左右两端进行扫描,只要还没有交替,就一直扫描
while (i <= j){
// 寻找直到比支点大的数
while (pivot > array[i]){
i++;
}
// 寻找直到比支点小的数
while (pivot < array[j]){
j--;
}
// 此时已经分别找到了比支点小的数、比支点大的数,它们进行交换
if (i <= j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
// 左边再做排序,直到左边剩下一个数
if (L < j){
quickSort(array, L, j);
}
// 右边再做排序,直到右边剩下一个数
if (i < R){
quickSort(array,i,R);
}
}
}
}
希尔排序
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
shellSort(array);
System.out.println(Arrays.toString(array));
}
/**
* 希尔排序
* @param array 待排序数组
*/
public static void shellSort(int[] array) {
// 增量每次都除于2
for (int step = array.length/2; step > 0; step/=2) {
// 从增量那组开始进行插入排序,直至完毕
for (int i = step; i < array.length; i++) {
int j = i;
int temp = array[j];
// j - step 就是代表与它同组隔壁的元素
while (j-step>=0 && array[j - step] > temp){
array[j] = array[j - step];
j = j - step;
}
array[j] = temp;
}
}
}
}
计数排序
public class Demo {
public static int[] countSort(int[] array) {
//1.获取数列的最大值
int max = array[0];
for(int i=1; i<array.length; i++){
if(array[i] > max){
max = array[i];
}
}
//2.根据数列最大值确定统计数组的长度
int[] countArray = new int[max+1];
//3.遍历数列,填充统计数组
for(int i=0; i<array.length; i++){
countArray[array[i]]++;
}
//4.遍历统计数组,输出结果
int index = 0;
int[] sortedArray = new int[array.length];
for(int i=0; i<countArray.length; i++){
for(int j=0; j<countArray[i]; j++){
sortedArray[index++] = i;
}
}
return sortedArray;
}
public static void main(String[] args) {
int[] array = new int[] {4,4,6,5,3,2,8,1,7,5,6,0,10};
System.out.println(Arrays.toString(countSort(array)));
}
}
基数排序
public class Demo {
//ascii码的取值范围
public static final int ASCII_RANGE = 128;
public static String[] redixSort(String[] array, int maxLength) {
// 排序结果数组,用于存储每一次按位排序的临时结果
String[] sortArray = new String[array.length];
// 从个位开始比较,一直比较到最高位
for (int k = maxLength-1; k>=0 ; k--) {
//计数排序的过程,分成三步:
//1.创建辅助排序的统计数组,并把待排序的字符对号入座,
//这里为了代码简洁,直接使用ascii码范围作为数组长度
int [] count = new int [ASCII_RANGE];
for (int i = 0; i < array.length; i++) {
int index = getCharIndex(array[i],k);
count[index]++ ;
}
// 2.统计数组做变形,后面的元素等于前面的元素之和
for (int i = 1; i < count.length; i++) {
count[i] = count[i] + count[i-1];
}
// 3.倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组
for (int i = array.length - 1; i >=0 ; i--) {
int index = getCharIndex(array[i], k);
int sortedIndex = count[index] - 1;
sortArray[sortedIndex] = array[i];
count[index]--;
}
// 下一轮排序需要以上一轮的排序结果为基础,因此把结果复制给array
array = sortArray.clone();
}
return array;
}
// 获取字符串第k位字符所对应的ascii码序号
private static int getCharIndex(String str, int k) {
// 如果字符串长度小于k,直接返回0,相当于给不存在的位置补0
if (str.length() < k+1){
return 0;
}
return str.charAt(k);
}
public static void main(String[] args) {
String[] array = {"qd","abc","qwe","hhh","a","cwe","ope"};
System.out.println(Arrays.toString(redixSort(array,3)));
}
}
堆排序
/**
* 1.把无序数组构建成二叉堆
* 2.循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶
*/
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
heapSort(array);
System.out.println(Arrays.toString(array));
}
/**
* 堆排序
* @param array 待调整的堆
*/
public static void heapSort(int[] array) {
// 把无序数组构建成二叉堆
for (int i = (array.length-2)/2; i >= 0; i--) {
downAdjust(array, i, array.length);
}
for (int i = array.length - 1; i > 0; i--) {
// 最后一个元素和第一个元素进行交换
int temp = array[i];
array[i] = array[0];
array[0] = temp;
// 下沉调整最大堆
downAdjust(array,0, i);
}
}
/**
* 下沉调整
* @param array 待调整的堆
* @param parentIndex 要下沉的父节点
* @param length 堆的有效大小
*/
private static void downAdjust(int[] array, int parentIndex, int length) {
// temp保存父节点值,用于最后的赋值
int temp = array[parentIndex];
int childIndex = 2*parentIndex + 1;
while (childIndex < length){
// 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
if (childIndex + 1 < length && array[childIndex + 1] > array[childIndex]){
childIndex++;
}
// 如果父节点小于任何一个孩子的值,直接跳出
if (temp >= array[childIndex]){
break;
}
// 无需真正交换,单向赋值即可
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2*childIndex + 1;
}
array[parentIndex] = temp;
}
}
桶排序
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
int[] sortedArray = bucketSort(array);
System.out.println(Arrays.toString(sortedArray));
}
/**
* 桶排序
* @param array 待排序数组
*/
public static int[] bucketSort(int[] array) {
// 1.得到数列的最大值和最小值,并算出差值d
int max = array[0];
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max){
max = array[i];
}
if (array[i] < min){
min = array[i];
}
}
int d = max - min;
// 2.初始化桶
int bucketNum = array.length;
ArrayList<LinkedList<Integer>> bucketList = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketList.add(new LinkedList<Integer>());
}
// 3.遍历原始数组,将每个元素放入桶中
for (int i = 0; i < array.length; i++) {
int num = ((array[i] - min)) * ((bucketNum - 1) / d);
bucketList.get(num).add(array[i]);
}
// 4.对每个桶内部进行排序
for (int i = 0; i < bucketList.size(); i++) {
// jdk底层采用了归并排序或归并排序的优化版本
Collections.sort(bucketList.get(i));
}
// 5.输出全部元素
int[] sortedArray = new int[array.length];
int index = 0;
for (LinkedList<Integer> list: bucketList) {
for (int item: list) {
sortedArray[index] = item;
index++;
}
}
return sortedArray;
}
}
给定一个字符串,找出不含有重复字符的最长字串
public class Demo {
public static int getLongSubString(String str) {
int n = str.length();
int res = 0;
int end=0,start=0;
Set<Character> set=new HashSet<>();
while(start<n && end<n){
if(set.contains(str.charAt(end))){
set.remove(str.charAt(start++));
}else{
set.add(str.charAt(end++));
res=Math.max(res,end-start);
}
}
return res;
}
public static void main(String[] args) {
System.out.println(getLongSubString("abcabcbb"));
}
}
List如何一边遍历,一边删除
public class Demo {
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
delete1(platformList);
System.out.println(platformList);
}
// 推荐使用(阿里)
public static void delete1(List<String> list) {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String platform = iterator.next();
if (platform.equals("博客园")) {
iterator.remove();
}
}
}
// for 循环正序遍历删除元素后,需要修正下标值
public static void delete2(List<String> list) {
for (int i = 0; i < list.size(); i++) {
String item = list.get(i);
if (item.equals("博客园")) {
list.remove(i);
// 删除元素后,要修正下下标的值
i = i - 1;
}
}
}
// for 循环倒序遍历删除元素后,不需要修正下标值
public static void delete3(List<String> list) {
for (int i = platformList.size() - 1; i >= 0; i--) {
String item = platformList.get(i);
if (item.equals("掘金")) {
platformList.remove(i);
}
}
}
}
找出数组中的第K大元素
public class Demo {
public static void main(String[] args) {
int[] array = new int[] {11,12,4,4,6,5,3,2,8,1,7,5,6,0,10};
System.out.println(findKthLargest(array, 3));
}
/**
* 数组中的第K大元素
* @param array 所有源数据
* @param k 第几大元素
* @return
*/
public static int findKthLargest(int[] array, int k) {
// PriorityQueue实现了数据结构堆
// 定义一个优先队列,优先队列中元素默认排列顺序是升序排列
PriorityQueue<Integer> minQueue = new PriorityQueue<>();
// 扫描完所有元素
for (int num: array){
// 若大于堆顶,则入堆
if (minQueue.size() < k || num > minQueue.peek()){
// 通过offer添加元素可以排序
minQueue.offer(num);
}
// 维护堆最小值 k
if (minQueue.size()>k){
// 删除堆顶
minQueue.poll();
}
}
// 返回堆顶, 即第K大元素
return minQueue.peek();
}
}
读取txt数据(大数据处理)
/**
* TXT文本中如据形如:
* 123|457|12|124
* 456|457|12|117
* 789
*/
public class Demo {
public static void main(String[] args) {
readTxtData("h:/20200323.txt");
}
/**
* 读取TXT数据
* @param filePath 文件路径
*/
public static void readTxtData(String filePath) {
// 定义存放数据的容器
List<Integer> list = new ArrayList();
try {
FileInputStream fileInputStream = new FileInputStream(new File(filePath));
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader br= new BufferedReader(inputStreamReader);
String str=null;
// 读取一行数据
while((str=br.readLine())!=null)
{
// 数据处理,并根据"|"分割取数据
dealData(str,"\\|",list);
}
inputStreamReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(list);
}
/**
* 数据处理
* @param str 待处理的数据
* @param regex 分割符
* @param list 存放数据的容器
*/
private static void dealData(String str, String regex, List<Integer> list) {
// 判断是否要根据数据分隔符拆分数据
if (regex == null || regex.equals("")){
// 把数据放入数组
list.add(Integer.valueOf(str));
}else {
// 根据数据分隔符拆分数据
String[] strings = str.split(regex);
for (String s: strings) {
// 把数据放入数组
list.add(Integer.valueOf(s));
}
}
}
}
10亿个数字里里面找最小的10个
// 利用最小堆获取TopK
public class TopK {
public static void main(String[] args) {
// 源数据
int[] data = {56,275,12,6,45,478,41,1236,456,12,546,45};
// 获取Top5
int[] top5 = topK(data, 5);
for(int i=0;i<5;i++) {
System.out.println(top5[i]);
}
}
// 从data数组中获取最大的k个数
private static int[] topK(int[] data,int k) {
// 先取K个元素放入一个数组topk中
int[] topk = new int[k];
for(int i = 0;i< k;i++) {
topk[i] = data[i];
}
// 转换成最小堆
MinHeap heap = new MinHeap(topk);
// 从k开始,遍历data
for(int i= k;i<data.length;i++) {
int root = heap.getRoot();
// 当数据大于堆中最小的数(根节点)时,替换堆中的根节点,再转换成堆
if(data[i] > root) {
heap.setRoot(data[i]);
}
}
return topk;
}
}
// 最小堆的实现
public class MinHeap{
// 堆的存储结构 - 数组
private int[] data;
// 将一个数组传入构造方法,并转换成一个小根堆
public MinHeap(int[] data) {
this.data = data;
buildHeap();
}
// 将数组转换成最小堆
private void buildHeap() {
// 完全二叉树只有数组下标小于或等于 (data.length) / 2 - 1 的元素有孩子结点,遍历这些结点。
// *比如上面的图中,数组有10个元素, (data.length) / 2 - 1的值为4,a[4]有孩子结点,但a[5]没有*
for (int i = (data.length) / 2 - 1; i >= 0; i--) {
// 对有孩子结点的元素heapify
heapify(i);
}
}
private void heapify(int i) {
// 获取左右结点的数组下标
int l = left(i);
int r = right(i);
// 这是一个临时变量,表示 跟结点、左结点、右结点中最小的值的结点的下标
int smallest = i;
// 存在左结点,且左结点的值小于根结点的值
if (l < data.length && data[l] < data[i]) {
smallest = l;
}
// 存在右结点,且右结点的值小于以上比较的较小值
if (r < data.length && data[r] < data[smallest]) {
smallest = r;
}
// 左右结点的值都大于根节点,直接return,不做任何操作
if (i == smallest){
return;
}
// 交换根节点和左右结点中最小的那个值,把根节点的值替换下去
swap(i, smallest);
// 由于替换后左右子树会被影响,所以要对受影响的子树再进行heapify
heapify(smallest);
}
// 获取右结点的数组下标
private int right(int i) {
return (i + 1) << 1;
}
// 获取左结点的数组下标
private int left(int i) {
return ((i + 1) << 1) - 1;
}
// 交换元素位置
private void swap(int i, int j) {
int tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
// 获取对中的最小的元素,根元素
public int getRoot() {
return data[0];
}
// 替换根元素,并重新heapify
public void setRoot(int root) {
data[0] = root;
heapify(0);
}
}
打印杨辉三角
public class Demo {
public static void main(String[] args) {
List<List<Integer>> lists = generateTriangleByRecursive(70);
lists.forEach(item -> System.out.println(item));
// printTriangle(lists);
}
/**
* 构建杨辉三角
* @param numRow 杨辉三角行数
* @return
*/
public static List<List<Integer>> generateTriangleByRecursive(int numRow) {
// 存放杨辉三角数据的容器(二维数组)
List<List<Integer>> list = new ArrayList<>();
// 备忘录
Map<Integer, Map<Integer, Integer>> cacheMap = new HashMap<>();
for (int i = 0; i < numRow; i++) {
// 存放杨辉三角每一行数据的容器
List<Integer> subList = new ArrayList<>();
for (int j = 0; j < i + 1; j++) {
// 把数据添加到容器中
subList.add(generateTriangleByRecursive(i, j, cacheMap));
}
// 把行数据容器添加到杨辉三角的容器中
list.add(subList);
}
return list;
}
/**
*
* @param i 第几行
* @param j 第几列
* @param cacheMap 备忘录
* @return
*/
private static int generateTriangleByRecursive(int i, int j, Map<Integer, Map<Integer, Integer>> cacheMap) {
// 备忘录是否存在该坐标值
if (cacheMap.containsKey(i) && cacheMap.get(i).containsKey(j)) {
return cacheMap.get(i).get(j);
}
int result;
// 递归约束条件(是否是每行的第一个或最后一个数)
if (j == 0 || j == i) {
result = 1;
} else {
//计算当前坐标值 f(x,y) = f(x-1,y-1) + f(x-1,y)
result = (generateTriangleByRecursive(i - 1, j - 1, cacheMap)
+ generateTriangleByRecursive(i - 1, j, cacheMap));
}
// 判断备忘录是否存在当前行数据备忘录
if (!cacheMap.containsKey(i)) {
Map<Integer, Integer> currentRowCache = new HashMap<>();
cacheMap.put(i, currentRowCache);
}
// 当前值添加到备忘录
cacheMap.get(i).put(j, result);
return result;
}
/**
* 打印杨辉三角
* @param lists 待打印的杨辉三角数据(二维数组)
*/
private static void printTriangle(List<List<Integer>> lists) {
// 杨辉三角高度
int numRow = lists.size();
for (int i = 0; i < numRow; i++) {
// 左边打印空格,打印等腰三角形
for (int j = 0; j < numRow-i; j++) {
System.out.print(" ");
}
for (int j = 0; j < lists.get(i).size(); j++) {
// 打印杨辉三角每一行的数据
System.out.print(lists.get(i).get(j) + " ");
}
// 换行
System.out.println();
}
}
}