代码编程
日常在牛客网上的练习
题目目录
反转数字
给你一个 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副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。
有如下规则:
- A为1,J为11,Q为12,K为13,A不能视为14
- 大、小王为 0,0可以看作任意牌
- 如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出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;
}
}