Java算法题练习--简单篇

代码编程

日常在牛客网上的练习

反转数字

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
你有注意到翻转后的整数可能溢出吗?因为给出的是32位整数,翻转可能会导致溢出,如果反转后的结果会溢出就返回 0。

public class Solution{
	public int reverse(int x){
		int result = 0;
		while(x!=0){
			int temp = x%10;
			int newResult = result*10 + temp;
			//判断是否溢出,如果溢出结果不一致
			if((newResult-temp)/10 != result){
				return 0;
			}
			result = newResult;
			x = x/10;
		}
		return result;
	}
}

进制转换

给定一个十进制数 M ,以及需要转换的进制数 N 。将十进制数 M 转化为 N 进制数。
当 N 大于 10 以后, 应在结果中使用大写字母表示大于 10 的一位,如 ‘A’ 表示此位为 10 , ‘B’ 表示此位为 11 。
若 M 为负数,应在结果中保留负号。

import java.util.*;
public class Solution{
	public String solve (int M, int N){
		//最大16位
		String s = "0123456789ABCDEF";
		StringBuffer sb = new StringBuffer();
		//这个b用来判断是不是负数,如果是正数为false,如果是负数为true
		boolean b = false;
		//M为负数,修改b,同时M取负,将M变成正数
		if(M<0){
			b = true;
			M = -M;
		}
		//循环拼接字符串,将M%N产生的余数拼接起来,同时M也要不断缩小M=M/N
		while(M!=0){
			sb.append(M%N);
			M = M/N;
		}
		//如果b为true,即M为负数,执行下面给字符串添加"-"
		if(b){
			sb.append("-");
		}
		//结果先将字符串反转,再打印字符串
		return sb.reverse().toString();
	}
}

二分查找

请实现有重复数字的有序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
示例1
输入
5,4,[1,2,4,4,5]
返回值
3
说明
输出位置从1开始计算

import java.util.*;
public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        int left = 0;//设置最左数
        int right = n-1;//设置最右数
        if(a[n-1]<v)
        {return n+1;}//判断有无这种数,这个数组的所有数比V小
        else{
        while(right>left){
            int mid = left+(right-left)/2;//防止溢出,中间数
            if(a[mid]>=v){//如果中间的数大于等于V,说明v在[left,mid]这个区域内
                right = mid;//同时把最右边right变为mid,就是将mid的值赋给right
            }else
                left = mid+1;//如果是中间的数小于V,说明v不可能是[left,mid]这个区间内,因为都是整型,所以最左值可以设为mid+1,在[mid right]这个区间内
        }
        return left+1; 根据题意最左值+1
    }
    }
}

两数之和

题目描述
给出一个整数数组,请在数组中找出两个加起来等于目标值的数,
你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的
假设给出的数组中只存在唯一解
例如:
给出的数组为 {20, 70, 110, 150},目标值为90
输出 index1=1, index2=2
示例1
输入
[3,2,4],6
返回值
[2,3]

import java.util.*;
public class Solution {
    /**
     * 
     * @param numbers int整型一维数组 
     * @param target int整型 
     * @return int整型一维数组
     */
    public int[] twoSum (int[] numbers, int target) {
        // write code here
        for(int i=0;i<numbers.length;i++){
            for(int j=i+1;j<numbers.length;j++){
                    if(target==numbers[i]+numbers[j]){
                        return new int[]{i+1,j+1};
                    }
            }
        }return null;
    }
}

求平方根

实现函数 int sqrt(int x).
计算并返回x的平方根(向下取整)

//第一种方法 直接判断
public int sqrt(int x){
	if(x==0) return 0;
	int i;
	for(i=1;i<x;i++){
	//平方根判断i*i<=x并且i+1 * i+1 >x 说明i的平方小于x,i+1的平方大于x,返回i
	if(i*i<=x&&(i+1)*(i+1)>x)
	return i;
	}
	return i;
}
//第二种方法 二分查询
public int sqrt (int x) {
        // write code here
        if(x<=0) return 0;
        int left = 1,right = x;
        while(true){
            int mid = (left+right)>>1;
            if(mid<=x/mid&&(mid+1)>x/(mid+1)){
                return (int) mid;
            }else if(mid<x/mid){
                left=mid+1;
            }else{
                right = mid-1;
            }
        }
    }

螺旋矩阵

给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。
输入:
[[1,2,3],[4,5,6],[7,8,9]]
复制
返回值:
[1,2,3,6,9,8,7,4,5]

public ArrayList<Integer> spiralOrder(int[][] matrix) {
         ArrayList<Integer> s = new ArrayList<>();
        if(s.length==0) return s;
         int top = 0;
        int down = matrix[0].length-1;
        int left = 0;
        int right = matrix.length-1;
        while(top<(matrix.length+1)/2 && left<(matrix[0].length)/2){
            for(int i=left;i<=right;i++){
                s.add(matrix[top][i]);
            }
            for(int i=top+1;i<=down;i++){
                s.add(matrix[i][right]);
            }
            for(int i=right-1;top!=down&&i>=left;i--){
                s.add(matrix[down][i]);
            }
            for(int i=down-1;left!=right&&i>=top+1;i--){
                s.add(matrix[i][left]);
            }
           ++top;
            --down;
            ++left;
            --right;
        }
    }

第一个只出现一次的字符

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
输入:
“google”
返回值:
4

import java.util.*
public class Solution{
	public int FirstNotRepeatingChar(String str){
		if(str.length()==0||str==null) return -1;
		HashMap<Character,Integer> map  = new HashMap<>();
		for(int i=0;i<str.length();i++){
		//contains方法,判断字符串是否有相同字符
		//charAt方法,返回索引处的字符
			if(!map.keySet().contains(str.charAt(i))){
				map.put(str.charAt(i),1);
			}else{
				map.put(str.charAt(i),map.get(str.charAt(i))+1);
			}
		}
		for(int i=0;i<str.length();i++){
			if(map.get(str.charAt(i))==1){
			 	return i;
			 }
		}
		return -1;
	}
}

回文数字

在不使用额外的内存空间的条件下判断一个整数是否是回文数字
提示:
负整数可以是回文吗?(比如-1)
如果你在考虑将数字转化为字符串的话,请注意一下不能使用额外空间的限制
你可以将整数翻转。但是,如果你做过题目“反转数字”,你会知道将整数翻转可能会出现溢出的情况,你怎么处理这个问题?

//最简单的方法,将数字转变成字符串,再通过反转字符串,字符串与之前比较
import java.util.*;
public class Solution{
	public boolean ifPalindrome(int x){
		if(x<0) return false;
		String str = Integer.toUnsignedString(x);
//reverse()反转字符串   toString输出 equals()字符串比较(只比较值,不比较地址)
		if((new StringBuffer(str).reverse()).toString.equals(str)){
			return true
		}else {
			return false;
		}
	}
}

最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

import java.util.*
public class Solution{
	//1.横向扫描
	public String longestCommonPrefix(String[] strs){
		//判断是否为空
		if(strs.length==0||strs==null)  return "";
		String first = strs[0];
		for(int i=1;i<strs.length;i++){
			first=LongPublicHead(first,strs[i]);
		}
		return first;
	}
	public String LongPublicHead(String str1,String str2){
		int index = 0;
		//判断小的字符串,公共前缀一定是以小的为准
		int len = Math.min(str1.length(),str2.length());
		//下标index小于长度,判断index的字符是否相等
		while(index<len&&str1.charAt(index)==str2.charAt(index)){
		index++;
		}
		//返回str1的index长度的字符串
		return str1.substring(0,index);
	}
	//2.使用排序,判断最大最小
	public String longestCommonPrefix(String[] strs){
		if(strs.length == 0|| strs == null) return "";
		//这个地方的排序可能跟自己想的不一样,建议自己看一下排序结果
		Arrays.sort(strs);
		int len = strs.length;
		int i = 0;
		int min = Math.min(strs[0].length(),strs[len-1].length());
		for(i = 0;i<len;i++){
			if(strs[0].charAt(i) != str[len-1].charAt(i))
			break;
		}
		return strs[0].charAt(i);
	}
	//3.纵向比较
	public String longestCommonPrefix(String[] strs){
		if(strs.length ==0 || strs==null) return "";
		int len = strs.length;
		int l = strs[0].length();
		for(int i=0;i<l;i++){
		char f = strs[i].length()
			for(int j=1;j<len;j++){
				if( i = str[j].length() ||strs[j].charAt(i) != f)
				return str[0].substring(0,i);
			}
		}
		return str[0];
	}
}

合并两个有序数组

描述
给出一个整数数组 和有序的整数数组 ,请将数组 合并到数组 中,变成一个有序的升序数组
注意:
1.可以假设 数组有足够的空间存放 数组的元素, 和 中初始的元素数目分别为 和 ,的数组空间大小为 +
2.不要返回合并的数组,返回是空的,将数组 的数据合并到里面就好了
3.数组在[0,m-1]的范围也是有序的

例1:
A: [1,2,3,0,0,0],m=3
B: [2,5,6],n=3
合并过后A为:
A: [1,2,2,3,5,6]

public class Solution {
    public void merge(int A[], int m, int B[], int n) {
        int i = m-1;//初始A数组的序号
        int j = n-1;//B数组的序号
        int index = m+n-1//合并后A的序号
        while(i>=0&&j>=0){
        	A[index--] = A[i]>B[j]?A[i--]:B[j--];
        	//这个地方Index,i,j都是自动减少
        }
        //如果B数组没有合并完,剩下的全部合并到A中
        while(j>=0){
			A[index--] = B[j--];//注意这里index,j是上面已经计算过的
		}
    }
}

在旋转过的有序数组中寻找目标值

给定一个整数数组nums,按升序排序,数组中的元素各不相同。
nums数组在传递给search函数之前,会在预先未知的某个下标 t(0 <= t <= nums.length-1)上进行旋转,让数组变为[nums[t], nums[t+1], …, nums[nums.length-1], nums[0], nums[1], …, nums[t-1]]。
比如,数组[0,2,4,6,8,10]在下标3处旋转之后变为[6,8,10,0,2,4]
现在给定一个旋转后的数组nums和一个整数target,请你查找这个数组是不是存在这个target,如果存在,那么返回它的下标,如果不存在,返回-1
输入:[6,8,10,0,2,4],10
输出:2

import java.util.*;
public class Solutiom{
	//第一种 二分查找(不排序)
	public int search(int []nums,target){
		//判断是否为空,为空时返回-1
		if(nums == null || nums.length == 0) return -1;
		int left = 0;
		int right = nums.length-1;
		while(right>=left){
			mid = left + (right-left)/2;
			//mid = left + ((right-left)>>1);这种更快一些
			if(nums[mid] == target) return mid;
			if(nums[mid]>nums[left]){//中间大于左边,说明左边这段是有序的
			/**判断目标值是否满足大于等于左边,并且小于中间,满足说明目标值在
			左段有序里面,否则就是就是在右端里面*/
				if(target<nums[mid] && target>=nums[left]){
					right = mid-1;
				}else{
					left = mid+1 ;
				}
			}else{//相反情况
				if(target>nums[mid] && target<=nums[right]){
					left = mid+1;
				}else{
					right = mid-1;
				}
			}
		}
		return -1;
	}
	//第二种 使用HashMap(排序+二分查找)
	public int search(int []nums,target){
		if(nums.length == 0 || nums == null) return -1;
		HashMap<Integer,Integer> map = new HashMap<>();
		Arrays.sort(map);
		int left = 0;
		int right = nums.length-1;
		while(right>left){
			mid = left + (right-left)/2;
			if(target == nums[mid]) return mid;
			if(target > nums[mid]) left = mid+1;
			if(target < nums[mid]) right = mid-1;
		}
		return -1;
	}
}

跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

public int jumpFloor(int target){
	if(target<=1) return 1; //当台阶小于等于1时,返回1
	int a=1,b=1,c=0;// a=jumpFloor[n-2]  b=jumpFloor[n-1] c=jumFloor[n]
	for(int i=2;i<=target;i++){
	//此处注意i<=target
	c=a+b;//跳台阶规律jumpFloor[n]=jumpFloor[n-1]+jumpFloor[n-2]
	a=b;//令a=jumpFloor[n-2],方便下次循环使用a
	b=c;//令b=jumpFloor[n-1],方便下次循环使用b
	}
	return c;
}
//最简单的方法 直接返回公式
public int jumpFloor(int target){
	if(target==1) return 1;
	if(target==2) return 2;
	return jumpFloor[target-1]+jumpFloor[target-2];
}

括号序列

给出一个仅包含字符’(’,’)’,’{’,’}’,’[‘和’]’,的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。

import java.util.*;
public class Solution {
    /**
     * 
     * @param s string字符串 
     * @return bool布尔型
     */
     //第一种使用栈
    public boolean isValid (String s) {
        // write code here
        Stack<Character> stack = new Stack();
        char[] chars = s.toCharArray();
        //遍历数组chars
        for(char c : chars){
        	//判断当前循环的字符是哪种如果是'('那么就将对应的')'压入栈顶,其他同理
            if(c == '('){
                stack.push(')');
            }else if(c == '['){
                stack.push(']');
            }else if(c == '{'){
                stack.push('}');
            //判断栈是否为空(这种是判断')'只有右符号的情况),或者 判断栈顶弹出的字符是否等于当前字符
            }else if(stack.isEmpty() || stack.pop() != c){
                return false;
            }
        }
        //如果栈为空,说明结果正确
        return stack.isEmpty();
    }
    //第二种方法,替换字符串
    public boolean isValid (String s) {
        // write code here
        boolean flag = true;
        while(flag){
            int len = s.length();
            //将"()"、"[]"、"{}"字符串替换成空的
            s = s.replace("()","");
            s = s.replace("[]","");
            s = s.replace("{}","");
            //判断循环开始的s的长度是否等于该次循环结束时的长度,如果长度一致说明替换失败
            if(len == s.length()){
                flag=false;
            }
        }
        //如果s的长度为0,说明全部替换成功
        return s.length()==0;
    }
}

买卖股票的最好时机(一次机会)

假设你有一个数组,其中第 i 个元素是股票在第 i 天的价格。
你有一次买入和卖出的机会。(只有买入了股票以后才能卖出)。请你设计一个算法来计算可以获得的最大收益。

public int maxProfit(int[] prices){
		int max = 0;
		int min = prices[0];
		for(int i=0;i<prices.length;i++){
			max = Math.max(prices[i]-min,max);
			min = Math.min(min,prices[i]);
		}
		return max;
}

股票(无限次交易)

假定你知道某只股票每一天价格的变动。
你最多可以同时持有一只股票。但你可以无限次的交易(买进和卖出均无手续费)。
请设计一个函数,计算你所能获得的最大收益。

import java.util.*;
public class Solution{
	public int maxProfit (int[] prices){
		int len = prices.length;
		int num = 0;
		for(int i=0;i<len-2;i++){
			//前后两数做差,如果大于0说明收益为正,可以添加,如果小于0,取0
			num += Math.max(prices[i+1]-prices[i],0);
		}
		return num;
	} 
}

换钱的最少货币数

给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。
如果无解,请返回-1.

import java.util.*;
public class Solution{
	public int minMoney (int[] arr, int aim){
		//因为钱数的取值是[0,aim],所以数组的大小为aim+1
		int [] count = new int[aim+1];	
		//用aim+1填充count数组
		Arrays.fill(count,aim+1);
		//初始化
		count[0] = 0;
		//遍历钱的面值
		for(int a : arr){
			//遍历数组,这个i可以看做当前钱数,同时count[i]是最少钱数
			for(int i =1;i<=aim;i++){
				if(i>=a){
					//取最小值,如果i-a是当前的钱数减去钱的面值,同时这个数量也得+1
					count[i] = Math.min(count[i],count[i-a]+1);
				}
			}
		}
		//返回结果,如果等于aim+1说明没有前面遍历数组的时候没有赋值(因为count数组初始化的时候所有值为aim+1)
		return count[aim] == aim+1 ? -1 : count[aim];
	}
}

合并二叉树

已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:都存在的结点,就将结点值加起来,否则空的位置就由另一个树的结点来代替。例如:
两颗二叉树是:
Tree 1
1
/ \
3 2
/
5

Tree 2
2
/
1 3
\
4 7

合并后的树为
3
/
4 5
/ \
5 4 7

import java.util.*;
public class Solution{
	public TreeNode mergeTrees (TreeNode t1, TreeNode t2){
		if(t1 == null) return t2;
		if(t2 == null) return t1;
		t1.val += t2.val;
		t1.left = mergeTrees(t1.left,t2.left);
		t2.right = mergeTrees(t1.right,t2.right);
		return t1;
	}
}

判断二叉树是否对称

//第一种递归
import java.util.*;
public class Solution{
	public boolean isSymmetre(TreeNode root){
		if(root==null) return true;
		return check(root.left,root.right);
	}
	public boolean check(TreeNode left,TreeNode right){
		//判断左右节点是否全为空,全为空是对称
		if(left==null&&right==null)
		return true;
		//判断左右节点有一个为空,只有一个为空,说明不对称
		if(left==null||right==null)
		return false;
		//返回判断条件 如果一直符合,最后返回true 中间有一次错误就返回false
		return left.val==right.val&& check(left.left,right.right)&& 
		check(left.right,right.left);
	}
}
//第二种迭代
import java.util.*;
public class Solution{
	public boolean isSymmetre(TreeNode root){
		if(root==null) return true;
		Queue<TreeNode> queue = new TreeNode<>();
		 queue.offer(root.left);
		 queue.offer(root.right);
		 while(!queue.isEmtry()){
			TreeNode left = queue.poll();
			TreeNode right = queue.poll();
			if(left==null&& right==null)
			return continue;
			if(left==null||right==null)
			return false;
			if(left.val!=right.val)
			return false;
			queue.offer(left.left);
			queue.offer(right.right);
			queue.offer(left.right);
			queue.offer(right.left); 
		}
		return true;
	}
}

二叉树镜像

操作给定的二叉树,将其变换为源二叉树的镜像。
比如: 源二叉树
8
/
6 10
/ \ /
5 7 9 11
镜像二叉树
8
/
10 6
/ \ /
11 9 7 5

public class Solution{
	//第一种,直接递归
	public TreeNode Mirror(TreeNode root){
	//先判断是否为空,会影响结果
		if(root==null) return root;
		TreeNode mid;
		mid = root.left;
		root.left = root.right;
		root.right = mid;
		Mirror(root.left);
		Mirror(root.right);
		return root;
	}
}

二叉树的最大深度

求给定二叉树的最大深度,
最大深度是指树的根结点到最远叶子结点的最长路径上结点的数量。
输入 {1,2} 返回 2
输入{1,2,3,4,#,#,5} 返回 3

import java.util.*;
public class Solution{
	//第一种方法 递归
	public int maxDepth(TreeNode root){
		if(root==null) return 0;
		return 1+Math.max(maxDepth(root.left),maxDepth(root.right));//每次都递增1
	}
	//第二种方法 使用队列 广度优先算法
	public int maxDepth(TreeNode root){
		if(root == null) return 0;
		//创建队列
		Queue<TreeNode> queue = new LinkedList<>();
		//将树添加到队列中
		queue.add(root);
		int result = 0;
		//判断条件 队列不为空
		while(!queue.isEmpty()){
			int size = queue.size(); //记录队列中的有效元素
			for(int i=0;i<size;i++){
				NodeTree node = queue.poll();//将出队元素添加到当前层的集合中
				//如果出队元素的左节点不为空,左节点添加到队列中
				if(node.left != null)
				queue.add(node.left);
				//如果出队元素的右节点不为空,右节点添加到队列中
				if(node.right != null)
				queue.add(node.right);
			}
			//每遍历完一层,结果+1
			result++;
		}
		return result;
	}
}

平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
注:我们约定空树是平衡二叉树。

public class Solution{
	boolean result = true ;
	public boolean tree(TreeNode root){
		if(root == null) return true;
		depth(root);
		return result;
	}
	public int depth(TreeNode node){
		if(node == null) return 0;
		int left = depth(node.left);
		int right = depth(node.right);
		if(Math.abs(left-right)>1)
		result = false;
		return Math.max(left,right);
	}
}

按之字形顺序打印二叉树

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
输入:
{1,2,3,#,#,4,5}
返回值:
[[1],[3,2],[4,5]]

输入:
{8,6,10,5,7,9,11}
复制
返回值:
[[8],[10,6],[5,7,9,11]]
使用两个栈的方式,第一个栈保存的是输出左右顺序(进栈的时候是右左顺序)的数据,第二个栈保存的是右左顺序(进站的时候是左右的顺序)的数据,在第一个栈内的元素出栈的同时,将其左右节点保存到第二个栈,循环完一次后,第二栈用相同的方式,一直循环到二叉树没有节点

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> result = new ArrayList();
        //判断二叉树是否为空
        if(pRoot==null) return result;
        //创建两个栈
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        //将根节点添加到栈1中
        stack1.add(pRoot);
        //栈1栈2只要有一个不为空说明二叉树没有遍历完,如果两个都为空了,说明二叉树遍历完毕
        while(stack1.size()>0||stack2.size()>0){
        	//创建一个list用来存储每次出栈的元素,每次循环都会创建一次
            ArrayList<Integer> list = new ArrayList();
            if(stack1.size()>0){
            	//创建一个对象保存栈的长度,用来控制循环次数,用来弹出栈内元素
                int size = stack1.size();
                for(int i=0;i<size;i++){
                    TreeNode pop = stack1.pop();
                    list.add(pop.val);
                    //如果该节点的左节点不为空,将其添加到栈2
                    if(pop.left!=null){
                        stack2.add(pop.left);
                    }
                    //如果该节点的右节点不为空,将其添加到栈2
                    if(pop.right!=null){
                        stack2.add(pop.right);
                    }
                }
                result.add(list);
                /**跳出循环的意义:如果不跳出本次循环,没有遍历完二叉树的情况下,会执行下面代码,
                  *但是下面代码中也会想List中添加元素,这样就会向结果中添加了栈1的两次元素
                  */
                continue;
            }
            if(stack2.size()>0){
                int size = stack2.size();
                for(int i=0;i<size;i++){
                    TreeNode pop = stack2.pop();
                    list.add(pop.val);
                    if(pop.right!=null){
                        stack1.add(pop.right);
                    }
                    if(pop.left!=null){
                        stack1.add(pop.left);
                    }
                }
                result.add(list);
                continue;
            }
         }
        return result;
    }
}

求路径

一个机器人在m×n大小的地图的左上角(起点)。
机器人每次向下或向右移动。机器人要到达地图的右下角(终点)。
可以有多少种不同的路径从起点走到终点?

public class Solution{
	public int path(int m,int n){
		if(m==0||n==0) return  0;
		int arr[][] = new int [m][n];
		for(int i=0;i<m;i++){
			arr[i][0] = 1;
		}
		for(int i =0;i<n;i++){
			arr[0][i] = 1;
		}
		for(int i=1;i<m;i++){
			for(int j=1;j<n;j++){
				arr[i][j] = arr[i-1][j]+arr[i][j-1];
			}
		}
		return arr[m-1][n-1]
	}
}

缺失数字

从0,1,2,…,n这n+1个数中选择n个数,找出这n个数中缺失的那个数,要求O(n)尽可能小。

import java.util.*;
public class Solution {
    public int solve (int[] a) {
        // write code here
        int len = a.length;
        int result = 0;
        for(int i=0;i<len;i++){
            if(a[i]!=i){
               result = i;
                return result;
            }
        }
        if(result == 0) result = len;
        return result;
    }
}

扑克牌顺子

现在有2副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。
有如下规则:

  1. A为1,J为11,Q为12,K为13,A不能视为14
  2. 大、小王为 0,0可以看作任意牌
  3. 如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出true,否则就输出false。
    例如:给出数据[6,0,2,0,4]
    中间的两个0一个看作3,一个看作5 。即:[6,3,2,5,4]
    这样这五张牌在[2,6]区间连续,输出true
    数据保证每组5个数字,每组最多含有4个零,数组的数取值为 [0, 13]
import java.util.TreeSet;
public class Solution {
    public boolean isContinuous(int [] n) {
        if (n.length < 5 || n.length > 5) {
            return false;
        }
        int num = 0;
        TreeSet<Integer> set = new TreeSet<> ();
        for (int i=0; i<n.length;i++) {
            if (n[i]==0) {
                num ++;
            } else {
                set.add(n[i]);
            }
        }
        if ((num + set.size()) != 5) {
            return false;
        }
        if ((set.last() - set.first()) < 5) {
            return true;
        }
        return false;
    }
}

未排序数组中累加和为给定值的最长子数组长度

给定一个无序数组arr, 其中元素可正、可负、可0。给定一个整数k,求arr所有子数组中累加和为k的最长子数组长度
输入:
[1,-2,1,1,1],0
返回值:
3
该题的思路:
sum为累加和,max为最长子数组长度,map的key为出现过的累加和,value为出现时数组的位置。
a[0…i]=arr[0]+…+arr[i] a[0…j]=arr[0]+…+arr[j]
a[0…i]-a[0…j] = a[j+1…i]
如果从0开始遍历那么 j+1>=1,a[0]没有遍历上。
如果sum-k存在,设其出现的位置是j,那么a[0…j] = sum -k;
a[j+1…i]=a[0…i] - a[0…j] = sum - (sum-k) = k ;
两者相差长度i - map.get(sum-k)
详细可见:https://blog.csdn.net/weixin_43982698/article/details/107135036

import java.util.*;
public class Solution{
	public int maxlenEqualK (int[] arr, int k){
		HashMap<Integer,Integer> map = new HashMap();
		int sum = 0;
		int max = 0;
		map.put(0,-1);
		for(int i=0;i<arr.length;i++){
			sum += arr[i];
			if(map.containsKey(sum-k)){
				max = Math.max(max,i-map.get(sum-k));
			}
			if(map.containsKey(sum){
				map.put(sum,i);
			)
		}
		return max;
	}
}

数字在升序数组中出现的次数

统计一个数字在升序数组中出现的次数。
输入:
[1,2,3,3,3,3,4,5],3
返回值:
4

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       if(array.length==0||array == null) return 0;
        int len = array.length;
        if(k<array[0] || k>array[len-1]) return 0;
        int num = 0;
        for(int i=0;i<len;i++){
            if(array[i]==k){
                num++;
            }
        }
        return num;
    }
}

数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000

import java.util.*;
public class Solution {
	//第一种方法使用哈希
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer,Integer> map = new HashMap();
        int len = array.length;
        for(int n : array){
            if(map.containsKey(n)){
                map.put(n,map.get(n)+1);
            }else{
                map.put(n,1);
            }
            if(map.get(n) > len/2){
                return   n;
            }
        }
        return -1;
    }
    //第二种
    public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
    	//max用来记录数量最多的值,默认给他数组第一个
        int max = array[0];
        //count用来记录出现数量,如果下一个是相同的就++,如果不一样就--,这样就出来max跟count了
        int count = 0;
        for(int n : array){
            if(max == n){
                count++;
            }else{
                count--;
                if(count == 0){
                    max = n;
                    count = 1;
                }
            }
        }
        //重新给count赋值0,因为要遍历一次确认max出现次数,用来判断是否大于数组的一半
        count = 0;
        for(int n : array){
            if(n==max){
                count++;
            }
        }
        if(count > array.length/2)
            return max;
        else
            return -1;
    }
}
}

反转链表

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        ListNode pre = null; //当前节点的前一节点
        ListNode next = null;//当前节点的后一节点
        while(head!=null){
            next = head.next; // 记录下一节点
            head.next = pre;//当前节点指向前一节点 实现饭庄
            pre = head;//前一节点向右走
            head = next;//当前节点向右走
        }
        return pre;
    }
}
//使用栈的方式(先进后出)
public class Solution {
    public ListNode ReverseList(ListNode head) {
        Stack<ListNode> stack = new Stack<>();//把链表节点放入栈中
        while(head!=null){
            stack.push(head);
            head = head.next;
        }
        if(stack.isEmpty()){
            return null;
        }
        ListNode node = stack.pop(); //将栈中节点全部出栈
        ListNode dummy = node;//连成一个新的链表
        while(!stack.isEmpty()){
            ListNode tempNode = stack.pop();//将弹出的节点赋值给tempNode
            node.next = tempNode;//将当前节点的下一节点指向tempNode
            node = node.next;//当前节点指向下一节丶
        }
        node.next=null;
        return dummy;
        
    }
}
//双链表
public class Solution {
    public ListNode ReverseList(ListNode head) {
       ListNode newHead = null; // 创建一个新链表
        while(head != null){
            ListNode temp = head.next; //创建一个中间保存节点,保存下一节点
            head.next = newHead;//将新链表的当前节点赋值给下一节点,下一节点为空
            newHead = head;//将头节点赋值给新链表的当前节点,新链表的尾是头节点
            head = temp;//将中间节点赋值给头结点,反转链表,指向下一节点
        }
        return newHead;
    }
}

单链表排序

给定一个无序单链表,实现单链表的排序(按升序排序)。

import java.util.*;
public class Solution{
	public ListNode sortInList (ListNode head) {
		//判断是否为空,方便之后的递归
		if(head==null||head.next==null)
		return head;
		//先用快慢指针找出中点
		ListNode slow = head;
		ListNode fast = head.next;
		while(fast!=null && fast.next!=null){
			fast = fast.next.next;
			slow = slow.next;
		}
		//分割链表,slow前一个,后一个
		ListNode newList = slow.next;
		slow.next = null;
		//连续分割链表一直分到不能再分
		ListNode left = sortInList(head);
		ListNode right = sortInList(newList);
		//创建排序的链表,value值为-1的链表
		ListNode Ihead = new ListNode(-1);
		ListNode res = Ihead;
		//进行归并排序
		while(left!=null && right!=null){
			if(left.val<right.val){
				Ihead.next = left;
				left = left.next;
			}else{
				Ihead.next = right;
				right = right.next;
			}
			Ihead = Ihead.next;
		}
		//进行判断将剩余的拼接上去
		Ihead.next = (left == null)? right:left;
		//因为第一个的值为-1,我们需要跳过第一个
		return res.next;
	}
}

删除有序链表中的重复元素

删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
例如:
给出的链表为1→1→2,返回1→2.
给出的链表为1→1→2→3→3,返回1→2→3.

import java.util.*;
public class Solution{
	public ListNode deleteDuplicates (ListNode head){
		if(head == null) return null;
		ListNode temp = head;
		while(temp.next!=null){
			if(temp.val == temp.next.val){
				temp.next = temp.next.next;
			}else{
				temp = temp.next;
			}
		}
		return head;
	}
}

判断链表中是否有环

1.快慢指针法
思路:快指针走两个节点,慢指针走一个节点,如果是环,指针会一直走下去,并且在一个节点相遇(这个可以用作判断跳出循环的条件,如果没有这个条件,环的话会是死循环),如果没有环,链表是可以走到头的,即快指针为空

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null)
            return false;
        ListNode slow = head;
        ListNode fast = head;
        while(fast!=null && fast.next !=null){
            slow=head.next;
            fast=head.next.next;
            if(slow==fast)
                return true;
        }
        return false;
    }
}

2.最快的方式 破坏表结构
//遍历链表的同时,让前一个节点的next指向head(或者是任意一个指定的内存),
//在后续的遍历中,如果有节点的当前next指向了head,则说明有环。
//破坏链表,达到最快

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode p =head; // 给p赋值头结点
        while(p!=null){
            ListNode s=p.next; //给s赋值p的下一节点
            if(s==head) return true;//s与头结点判断
            p.next = head;
            //给p的下一节点赋值头节点(区别下一次循环的p的下一节点)
            p=s;//给P赋值s(就是p的下一节点)
        }
        return false;
    }
}

两个链表的第一个公共结点

public class Solution{
	public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2){
		//判断是否为空,只要有一个链表为空,返回空
		if(pHead1 == null || pHead2 == null) return null;
		//创建两个链表,进行遍历
		ListNode n1 = pHead1;
		ListNode n2 = pHead2;
		while(n1!=n2){
		//向后遍历
			n1=n1.next;
			n2=n2.next;
		//避免死循环,提前结束
			if(n1!=n2){
				if(n1==null) n1=pHead2;
				if(n2==null) n2=pHead1;
			}
		}
		//n1为公共结点的开始
		return n1;
	}
	
	public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    if (pHead1 == null || pHead2 == null) return null;
    ListNode p1 = pHead1;
    ListNode p2 = pHead2;
    while (p1 != p2) {
        p1 = p1 == null ? pHead2 : p1.next;
        p2 = p2 == null ? pHead1 : p2.next;
    }
    return p1;
}
}

合并有序链表

将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的,且合并后新链表依然有序。

import java.util.*;
public class Solution{
	 public ListNode mergeTwoLists (ListNode l1, ListNode l2){
	 	//判断是否为空,如果为空,直接返回另一链表
	 	if(l1==null) return l2;
	 	if(l2==null) return l1;
	 	//创建头节点head,值为l1,l2值比较小的那个
	 	ListNode head = (l1.val<= l2.val)? l1:l2;
	 	//用来走向下一节点
	 	ListNode tail = head;
	 	//判断头结点是哪个,是头结点对应的数组就往下走
	 	l1 = (head==l1)? l1.next:l1;
	 	l2 = (head==l2)? l2.next:l2;
	 	while(l1!=null && l2!=null){
	 		//判断l1,l2当前节点的值谁小,小的那个赋给tail,并且对应链表往下走
	 		if(l1.val<=l2.val){
				tail.next = l1;
				l1 = l1.next;	 		
	 		}else{
	 			tail.next = l2;
	 			l2 = l2.next;
	 		}
	 		//tail走向下一节点
	 		tail = tail.next;
	 	}
	 	//判断是否有没遍历完的数组
	 	tail.next = (l1==null)? l2:l1;
	 	return head;
	 }
}

判断一个链表是否是回文结构

给定一个链表,请判断该链表是否为回文结构。

import java.util.*;
public class Solution{
//第一种通过数组的方式,第二种使用栈类似于这个第一种
	public boolean isPail(ListNode head){
		ArrayList<Integer> arr = new ArrayList<>();
		while(head!=null){
			arr.add(head.val);
			head = head.next;
		}
		for(int i = 0;i<arr.size();i++){
			if(!arr.get(i).equals(arr.get(arr.size-1-i))){
				return false;
			}
		}
		return true;
	}
//第三种,双指针
	public boolean isPail(ListNode head){
		ListNode fast=head,slow=head;
		while(fast!=null&&fast.next!=null){
			fast = fast.next.next;
			slow = slow.next;
		}
		if(fast!=null){
			slow = slow.next;
		}
		slow = re(slow);
		fast = head;
		while(slow!=null){
			if(slow.val!=fast.val){
				return false;
			}
			slow = slow.next;
			fast = fast.next;
		}
		return true;
	}
	public ListNode re(ListNode head){
		ListNode pre = null
		while(head!=null){
			ListNode next = head.next;
			head.next = pre;
			pre = head;
			head = next;
		}
		return pre;
	}
}

删除链表的倒数第n个节点

给定一个链表,删除链表的倒数第 nn 个节点并返回链表的头指针
例如,
给出的链表为: 1\to 2\to 3\to 4\to 51→2→3→4→5, n= 2n=2.
删除了链表的倒数第 nn 个节点之后,链表变为1\to 2\to 3\to 51→2→3→5.

备注:
题目保证 nn 一定是有效的
请给出请给出时间复杂度为\ O(n) O(n) 的算法

import java.util.*;
public class Solution{
	public ListNode removeNthFromEnd (ListNode head, int n){
		ListNode fast = head;
		ListNode slow = head;
		for(int i=0;i<n;i++){
			fast = fast.next;
		}
		//判断是否删除的是头节点
		if(fast==null){
			return head.next;
		}
		while(fast.next!=null){
			fast = fast.next;
			slow = slow.next;
		}
		//fast比slow快走n个节点,fast走完,slow的下个节点就是应删除节点
		slow.next = slow.next.next;
		return head;
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值