文章目录
如何判断一个字符串是数字
public class Main11 {
public static void main(String[] args) {
String s1 = "-12418413212";
System.out.println(isInteger(s1));
}
//只能判断正整数
public static boolean isNumeric(String str) {
for (int i = str.length(); --i >= 0;) {
if(!Character.isDigit(str.charAt(i))){
return false;
}
}
return true;
}
//正则,速度快
public static boolean isInteger(String str) {
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
return pattern.matcher(str).matches();
}
}
有1亿个数字其中有2个是重复的,快速找到该数字——位图
考查位图,把数字值直接映射到数组下标,这里重复的数字只有两次,为了空间最优,就用位图bit来表示(只有0和1),1byte=8bit,1byte可以存储8个数字的计数,所以建立一个byte数组用来计数:
byte[] bucket=new byte[(最大值-最小值)/8+1];
public class findRepeatable{
public static void main(String[] args){
int[] arr=new int[100000000];//1亿长度
for(int i=0;i<arr.length;i++){
arr[i]=i+1;
}
arr[99999999]=2020;
long time=new Date().getTime();
int res = core(arr);
long time2=new Date().getTime();
System.out.println("repeat number:"+res);
System.out.println("millisecond:"+(time2-time));
}
public static int core(int[] arr){
//O(N)的时间或者最小值和最大值
int min=arr[0];
int max=arr[0];
for(int i=0;i<arr.length;i++){
if(arr[i]<min)
min=arr[i];
if(arr[i]>max)
max=arr[i];
}
//空间复杂度O(N)
byte[] bucket=new byte[(max-min)/8+1];
for(int i=0;i<arr.length;i++){
int num=arr[i];
int j=(num-min)/8;
int k=(num-min)%8;
if(((bucket[j]>>k)&1)>0){//重复了
System.out.println("Number of repeats:"+num);
break;
}else{
bucket[j]|=(1<<k);
}
}
}
}
从10万个数中找10个最大的数
对海量数据处理,一般是先分块,然后处理后合并
1.先用某种规则分块
2.为了找到最大的10个元素,则用大小为10的最小堆
3.最后将每一块再进行合并
300. 最长上升子序列
动态规划,时间复杂度O(n^2)
class Solution {
public int lengthOfLIS(int[] nums){
if(nums.length==0) return 0;
int[] dp=new int[nums.length];
Arrays.fill(dp,1);
int res=0;
for(int i=0;i<nums.length;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
res=Math.max(res,dp[i]);
}
return res;
}
}
二分查找,时间复杂度O(nlog(n))
定义一个tail 数组,其中 tail[i] 表示长度为 i + 1 的上升子序列的末尾最小是几。
class Solution {
public int lengthOfLIS(int[] nums){
int len = nums.length;
if(len <= 1) return len;
// tail 数组的定义:tail[i] 表示长度为 i + 1 的上升子序列的末尾最小是几
int[] tail = new int[len];
tail[0] = nums[0];
//end表示最后一个被赋值的索引
int end = 0;
for (int i = 1; i < len; i++) {
//如果新来的数字比tail尾数大的话,直接添加进来
if(nums[i] > tail[end]){
end++;
tail[end] = nums[i];
}else{//否则使用二分查找法找到第一个比该数字大的数然后更新,如果相等就不用更新
int left = 0;
int right = end;
while (left < right){
int mid = (left + right) >>> 1;
if(tail[mid] < nums[i]){
left = mid + 1;
}else{
right = mid;
}
}
//这里的tail[left] >= nums[i]
tail[left] = nums[i];
}
}
return end + 1;
}
}
150. 逆波兰表达式求值
逆波兰表达式:逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
用Stack写,容易理解
public class No150 {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (String s : tokens){
if(s.equals("+")){
stack.push(stack.pop() + stack.pop());
}else if(s.equals("-")){
stack.push(- stack.pop() + stack.pop());
}else if(s.equals("*")){
stack.push(stack.pop() * stack.pop());
}else if(s.equals("/")){
int num = stack.pop();
stack.push(stack.pop() / num);
}else {
stack.push(Integer.parseInt(s));
}
}
return stack.pop();
}
//switch case代替if else 可以优化效率
public static int evalRPN2(String[] tokens) {
Stack<Integer> numStack = new Stack<>();
Integer op1, op2;
for (String s : tokens) {
switch (s) {
case "+":
op2 = numStack.pop();
op1 = numStack.pop();
numStack.push(op1 + op2);
break;
case "-":
op2 = numStack.pop();
op1 = numStack.pop();
numStack.push(op1 - op2);
break;
case "*":
op2 = numStack.pop();
op1 = numStack.pop();
numStack.push(op1 * op2);
break;
case "/":
op2 = numStack.pop();
op1 = numStack.pop();
numStack.push(op1 / op2);
break;
default:
numStack.push(Integer.valueOf(s));
break;
}
}
return numStack.pop();
}
}
用数组模拟栈来优化,很巧妙,数组最大长度tokens.length / 2 + 1
class Solution {
public int evalRPN(String[] tokens) {
int[] numStack = new int[tokens.length / 2 + 1];
int index = 0;
for (String s : tokens) {
switch (s) {
case "+":
numStack[index - 2] += numStack[--index];
break;
case "-":
numStack[index - 2] -= numStack[--index];
break;
case "*":
numStack[index - 2] *= numStack[--index];
break;
case "/":
numStack[index - 2] /= numStack[--index];
break;
default:
numStack[index++] = Integer.parseInt(s);
break;
}
}
return numStack[0];
}
}
406. 根据身高重建队列
贪心算法:
首先排序,身高降序排列,索引升序排列
然后逐个放到队列中去,索引值等于他们的k值
最后返回输出的队列
public class No406 {
public int[][] reconstructQueue(int[][] people) {
// Arrays.sort(people, new Comparator<int[]>() {
// @Override
// public int compare(int[] o1, int[] o2) {
// //身高降序排列,索引升序排列
// return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
// }
// });
Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);
List<int[]> output = new LinkedList<>();
for (int[] p : people){
output.add(p[1], p);
}
return output.toArray(new int[0][2]);
}
}
字符串压缩
190. 颠倒二进制位
98. 验证二叉搜索树
class Solution {
// long pre=Long.MIN_VALUE;
// public boolean isValidBST(TreeNode root) {
// if(root==null) return true;
// if(!isValidBST(root.left)){
// return false;
// }
// if(root.val<=pre){
// return false;
// }
// pre=root.val;
// return isValidBST(root.right);
// }
public boolean isValidBST(TreeNode root) {
if(root == null) return true;
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;//前驱指针
while (root != null || !stack.isEmpty()){
while (root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
//处理最左边的这个节点
if(pre != null && root.val <= pre.val) return false;
pre = root;
root = root.right;
}
return true;
}
}
518 零钱兑换——背包问题
class Solution {
//完全背包问题
//状态:背包的容量,可选择的物品
//选择:装或者不装
//dp的含义:dp[i][j]表示只用前i个硬币的面值,凑出金额j有dp[i][j]种方法
//base case:凑出的⽬标⾦额为0
//状态转移方程:(1)不把第i个物品装进去:dp[i][j]=dp[i-1][j];(2)装进去dp[i][j]=dp[i][j-coins[i-1]]
public int change(int amount, int[] coins) {
int n=coins.length;
int[] dp=new int[amount+1];
dp[0]=1;//base case
for(int i=0;i<n;i++){//对应数组coins[i-1]
for(int j=1;j<=amount;j++){
if(j-coins[i]>=0){
dp[j]=dp[j]+dp[j-coins[i]];
}
}
}
return dp[amount];
public int change(int amount, int[] coins) {
int n=coins.length;
int[][] dp=new int[n+1][amount+1];
for(int i=0;i<=n;i++){
dp[i][0]=1;//base case,如果凑出的⽬标⾦额为 0,那么“⽆为⽽治”就是唯⼀的⼀种凑法。
}
for(int i=1;i<=n;i++){//第i类硬币,对应数组coins[i-1]
for(int j=1;j<=amount;j++){
if(j-coins[i-1]>=0){
dp[i][j]=dp[i-1][j]+dp[i][j-coins[i-1]];
}else{
dp[i][j]=dp[i-1][j];
}
}
}
return dp[n][amount];
}
}
695. 岛屿的最大面积
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int res=0;
if(grid.length==0||grid[0].length==0) return res;
for(int row=0;row<grid.length;row++){
for(int col=0;col<grid[0].length;col++){
if(grid[row][col]==1){
int area=Area(grid,row,col,grid.length,grid[0].length);
res=Math.max(area,res);
}
}
}
return res;
}
public int Area(int[][] grid,int i,int j,int rows,int cols){
//base case,越界或者已经访问过
if(i<0||j<0||i>rows-1||j>cols-1||grid[i][j]!=1){
return 0;
}
//访问过,直接赋值2
grid[i][j]=2;
//四周发散
return 1+Area(grid,i-1,j,rows,cols)+Area(grid,i+1,j,rows,cols)+Area(grid,i,j-1,rows,cols)
+Area(grid,i,j+1,rows,cols);
}
}
264. 丑数
148. 排序链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
// 获取链表长度
int len = listNodeLength(head);
ListNode dummy = new ListNode(-1);
dummy.next = head;
//两两归并,四四归并,八八归并,需要循环logn次
for (int i=1;i<len;i<<=1){
ListNode pre = dummy;
ListNode cur = dummy.next;
while (cur!=null){//遍历整个链表,cur是每次移动两组链表
ListNode left = cur;
ListNode right = split(left,i);
cur = split(right,i);//cur相当于跳过两个分割链表的长度了
pre.next = mergeTwoLists(left,right);
while (pre.next!=null){
pre = pre.next;
}
}
}
return dummy.next;
}
/**
* 获取链表长度
* @param head
* @return
*/
public int listNodeLength(ListNode head){
int len=0;
ListNode cur=head;
while (cur!=null){
len++;
cur=cur.next;
}
return len;
}
/**
* 根据步长分割链表
* @param head
* @param step
* @return
*/
public ListNode split(ListNode head,int step){
if(head==null) return null;
for (int i=1;head.next!=null && i<step;i++) {//防止越界
head=head.next;
}
ListNode right=head.next;
head.next=null;//要断开链表,这步别漏
return right;
}
/**
* 合并两个链表
* @param l1
* @param l2
* @return
*/
public ListNode mergeTwoLists(ListNode l1,ListNode l2){
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while (l1!=null && l2!=null){
if(l1.val<l2.val){
cur.next=l1;
l1=l1.next;
}else{
cur.next=l2;
l2=l2.next;
}
cur=cur.next;
}
cur.next=l1==null?l2:l1;
return dummy.next;
}
}