0到n-1范围内,长度为n,若不重复,必定涵盖所有0到n-1的值,将数组进行排序,小的数字放在数组的前面,排序过程中,如果碰到相同的数字输出。
复杂度分析:时间复杂度O(nlogn)/空间复杂度O(1)
LeetCode没通过
0到n-1范围内,长度为n,Java中的set特性为无序且不重复,创建一个和数组等长的HashSet,将数组的数依次放进set,添加失败时,输出该重复数。HashSet中的add()方法,添加成功时返回true
复杂度分析:时间复杂度O(n)/空间复杂度O(n)
0到n-1范围内,长度为n,若不重复,必定涵盖所有0到n-1的值,将数组调整为nums[i] = i即元素值等于其索引值的情况。
复杂度分析:时间复杂度O(n)/空间复杂度O(1)
如图所示①②为两个较大值,从①出发,进行比较,比target大,上移减小,比target小,右移增大。
String的方法replace(“AA”,“BB”)
String生成之后不可以更改,用一个StringBuilder来接受String,charAt(i)接收字符,判断是否为空格,然后替换
使用Stack,push()和pop()方法
先获取链表的长度,然后再使用数组来接受链表的值
前序遍历:先根节点,在左子节点,最后右子节点
中序遍历:先左子节点,在根节点,最后右子节点
后序遍历:先左节点,在右子节点,最后根节点
前序遍历:10,6,4,8,14,12,16
中序遍历:4,6,8,10,12,14,16
后序遍历:4,8,6,12,16,14,10
前序遍历中,第一个是根节点,中序遍历中,在根节点前的就是左子树,在根节点后的就是右子树。在左、右子树中重复上述过程(递归),就可以生成最终的树
public class Resolution{
public TreeNode buildTree(int[] preorder, int[] inorder){
Map<Integer,Integer> map = new HashMap();
for(int i = 0; i < inorder.length;i++){
map.put(inorder[i],i);
}
return builderTreeHelper(preorder,0,preorder.length,inorder,0,inorder.length,map);
}
private TreeNode buildTreeHelper(int[] preorder,int p_start,int p_end,int[] inorder,int i_start,int i_end,Map<Integer,Integer> map){
if(p_start == p_end){
return null;
}
int root_val = preorder[p_start];
TreeNode root = new TreeNode(root_val);
int i_dx = map.get(root_val);
int left_num = i_dx - i_start;
root.left = buildTreeHelper(preorder,p_start+1,p_start+1+left_num,inorder,i_start,i_dx,map);
root.right = builderTreeHelper(preorder,p_start+1+left_num,p_end,inorder,i_dx+1,i_end,map);
return root;
}
}
栈:特点是后进先出,最后被压入(push)、最先被弹出(pop)
队列:特点是先进先出
两个栈模拟一个队列:
①模拟向队列插入元素:将元素依次压入stack1栈内
②模拟向队列删除元素:因为栈的元素时后进先出,所以现在位于stack1栈顶的时队列的队尾,将stack1栈的所有元素依次弹出、压入stack2内,这样stack2就是队头是栈顶,依次pop即可
/*
该实现方法效率不佳,原因在于每次执行完一次删除操作之后,就又还原了队列
*/
public CQueue{
//声明变量stack1、stack2
private Stack<Integer> stack1;
private Stack<Integer> stack2;
public cQueue(){
//构造器内实例化stack1、2
stack1 = new stack<>();
stack1 = new stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead(){
if(stack1.isEmpty())
return -1;
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
int val = stack2.pop();
while(!stack2.isEmpty()){
stack1.push(stack2.pop())
}
return val;
}
}
/*
高效方法:删除之后,不还原,如果要删除,就接着删除,新加进来的元素放在stack1中,stack2中的元素没了以后,在进行一次整体压入
*/
class CQueue {
private Stack<Integer> stack1;
private Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if(stack2.isEmpty()){
if(stack1.isEmpty()){
return -1;
}
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
class CQueue {
int[] arr = new int[10000];
int end = 0;
int start = 0;
public CQueue() {
}
public void appendTail(int value) {
/*
Java里面 i++和++i之间的区别:
i++ 先赋值在运算,例如 a=i++,先赋值a=i,后运算i=i+1,所以结果是a==1
++i 先运算在赋值,例如 a=++i,先运算i=i+1,后赋值a=i,所以结果是a==2
*/
arr[end++] = value;
}
public int deleteHead() {
if(start == end){
return -1;
}else{
return arr[start++];
}
}
}
①向栈内压入元素:
直接向队列1加入元素即可
②栈向外弹出元素:
队列1将前n-1个元素按顺序删除并添加至队列2中,队列1中第n个元素返回。
方法1:递归(力扣官网不通过,超时)
class Fibnum{
public int getFibnum(int n){
if(n < 0){
return -1;
}else if(n ==0 || n == 1){
return n;
}else{
return getFibnum(n-1)+getFibnum(n-2);
}
}
}
方法2:把递归用遍历来实现
方法①存在的问题是:
当 n = 10 时,需要计算n = 9和n = 8时结果
对于n = 9:
需要计算n = 8(重复计算)和n = 7,在项数增大时,会出现很多个重复计算过的中间结果,浪费了资源。
这就是导致力扣不通过的原因
class Solution{
public int getFib(int n){
int a = 0;
int b = 1;
int fibRes = 0;
if(n < 0){
return -1;
}
if(n == 0 || n == 1){
return n;
}
for(i = 2;i <= n;i++){
fibRes = a + b;
a = b;
b = fibRes;
}
return fibRes;
}
}
一级台阶时,有1种跳法
两级台阶时,有2种跳法
台阶数 n>= 2时:
情况① 第一次先跳一级,剩下n-1级台阶数的跳法
情况② 第一次先跳两级,剩下n-2级台阶数的跳法
综上:f(n) = f(n-1) + f(n-2)
https://blog.csdn.net/weixin_38328533/article/details/111726180
若在排序和部分排序的数组中,查找某数或者统计某个数出现的次数,一般使用二分查找
旋转数组是部分排序,两个排序的子数组,用二分法分别对两个子数组进行查找。
两个特殊情况:
原数组: 0,1,1,1,1
旋转数组1:1,0,1,1,1
旋转数组2:1,1,1,0,1
在最前面的指针、最后面的指针、中间的指针,三者指的数组的数是相同的时候,此时二分法失效,需要使用顺序查找。
class Solution {
public int minArray(int[] numbers) {
if(numbers.length < 0){
return -1;
}
int start = 0;
int end = numbers.length - 1;
int mid = 0;
while(numbers[start] >= numbers[end]){
if(start + 1 == end){
mid = end;
break;
}
mid = (start + end)/2;
if(numbers[start] == numbers[mid] && numbers[end] == numbers[mid]){
return FindInOrder(numbers,start,end);
}
if(numbers[start] <= numbers[mid]){
start = mid;
}else if(numbers[end] >= numbers[mid]){
end = mid;
}
}
return numbers[mid];
}
private int FindInOrder(int[] numbers,int start,int end){
int res = numbers[start];
for(int i = start + 1;i < end;i++){
if(res > numbers[i]){
res = numbers[i];
}
}
return res;
}
}
class Solution {
private boolean[][] visited;
public boolean exist(char[][] board, String word) {
visited = new boolean[board.length][board[0].length];
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[i].length; j++){
if(word.charAt(0) == board[i][j] && search(board,word,i,j,0)){
return true;
}
}
}
return false;
}
private boolean search(char[][] board, String word, int i, int j, int index){
if(index == word.length())
return true;
//如果当前的方格不属于word的序列或者被走过
if(board[i][j] != word.charAt(index) || visited[i][j])
return false;
if(i < 0 || i >= board.length || j < 0 || j >= board[i].length)
return false;
//标记已走过
visited[i][j] = true;
boolean up = search(board,word,i-1,j,index+1);
boolean down = search(board,word,i+1,j,index+1);
boolean left = search(board,word,i,j-1,index+1);
boolean right = search(board,word,i,j+1,index+1);
if(up || down || left || right)
return true;
//没有走过
visited[i][j] = false;
return false;
}
}
使用回溯法,方格看为m*n的矩阵
class Solution {
int count = 0;
public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
backTrack(visited,0,0,m,n,k);
return count;
}
private void backTrack(boolean[][] visited, int i, int j, int m, int n, int k){
if(i < m && j < n && !visited[i][j] && (i/10 + i%10 + j/10 + j%10)<= k){
count++;
visited[i][j] = true;
backTrack(visited, i+1, j, m, n, k);
backTrack(visited, i, j+1, m, n, k);
}
}
}
class Solution {
public int cuttingRope(int n) {
if(n <= 3){
return n-1;
}
int a = n/3;
int b = n%3;
if(b == 0)
return (int)Math.pow(3,a);
if(b == 1){
a = a-1;
return (int)Math.pow(3,a)*4;
}
return (int)Math.pow(3,a-1) *3 *2;
}
}
class Solution {
public int cuttingRope(int n) {
int[] res = new int[n+1];
if(n < 2)
return n-1;
res[2] = 1;
for(int i = 3; i <= n; i++){
for(int j = 2; j < i; j++){
res[i] = Math.max(res[i],Math.max((j * res[i-j]),j * (i - j)));
}
}
return res[n];
}
}
class Solution {
public int cuttingRope(int n) {
if(n <= 3){
return n-1;
}
long res = 1;
int a = n/3;
int b = n%3;
for(int i = 1; i < a; i++){
res = (res *3)% 1000000007;
}
if(b == 0)
return (int) (res *3 %1000000007);
if(b == 1)
return (int) (res *4 %1000000007);
return (int)(res *3*2 %1000000007);
}
}
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count = 0;
while(n != 0){
count += n & 1;
//这里移位操作需要使用 >>>
// >> 代表有符号数移位
// >>>代表无符号移位
//如果使用>>,可能会导致死循环,因为负数的首位是1,右移补1,无法退出循环
n = n >>> 1;
}
return count;
}
}
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count = 0;
while(n != 0){
//假设 n = 1100; n-1 = 1011;
//n & (n-1) = 1000;
//n & (n-1)自动去除最低位上的1,在不等于0之前,去除几次,加几次,就可以得到1的个数
n= n & (n-1);
count++;
}
return count;
}
}
//leetcode 官网不通过
class Solution {
public double myPow(double x, int n) {
double res = 1.0;
if(x == 0 && n == 0){
return -1.0;
}
if(x == 0){
return 0.0;
}
if(n < 0){
n = -n;
x = 1.0/x;
}
for(int i = 1; i <= n; i++){
res = res * x;
}
return res;
}
}
public class Soulution{
public double myPow(double x, int n){
double res = 1.0;
//x为0,n<0时,需要x为倒数在做n次方,0不能为分母,故报错;
if(x == 0 && n < 0){
return -1.0;
}
if(n == -1){
x = 1.0/x;
}
if(n == 0){
return 1.0;
}
if(n == 1){
return x;
}
double half = myPow(x,n/2);
double mod = myPow(x,n%2);
res = half * half * mod;
return res;
}
}
class Solution {
public int[] printNumbers(int n) {
int num = (int) Math.pow(10,n)-1;
int[] res = new int[num];
for(int i = 0; i < num; i++){
res[i] = i+1;
}
return res;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode findHead = new ListNode(0);
findHead.next = head;
ListNode tmp = findHead;
while(tmp.next != null){
if(tmp.next.val == val){
tmp.next = tmp.next.next;
break;
}
tmp = tmp.next;
}
return findHead.next;
}
}
字符串表示整数的形式为:A.BeEC
A代表整数部分,B代表小数部分,C代表指数部分
A,C可为 0-9、+ 、-
B只能为0-9
class Solution {
public boolean isNumber(String s) {
//trim()方法会去掉两边多余的空格
s = s.trim();
boolean hasNum = false;
boolean hasE = false;
boolean hasPoint = false;
boolean hasNumAfterE = true;
int len = s.length();
if(len == 0 || s == null){
return false;
}
for(int i = 0; i < len; i++){
char charAtIndexI = s.charAt(i);
if(charAtIndexI >= '0' && charAtIndexI <= '9'){
hasNum = true;
hasNumAfterE = true;
}else if(charAtIndexI == '.'){
if(hasPoint || hasE){
return false;
}
hasPoint = true;
}else if(charAtIndexI == 'e' || charAtIndexI == 'E'){
if(!hasNum || hasE){
return false;
}
hasE = true;
hasNumAfterE = false;
}else if(charAtIndexI == '+' || charAtIndexI == '-'){
if(i != 0 && s.charAt(i-1) != 'e' && s.charAt(i-1) != 'E'){
return false;
}
}else{
return false;
}
}
return hasNumAfterE && hasNum;
}
}
使用两个指针,一个从前往后找偶数,一个从后往前找奇数,找到以后,交换位置,知道两个指针换位置
class Solution {
public int[] exchange(int[] nums) {
int start = 0;
int end = nums.length - 1;
int tmp;
while(start < end){
if(start < end && nums[start] % 2 == 1){
start++;
}
if(start < end && nums[end] % 2 == 0){
end--;
}
if(start < end){
tmp = nums[end];
nums[end] = nums[start];
nums[start] = tmp;
}
}
return nums;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
if(head == null){
return head;
}
while(k > 0){
fast = fast.next;
k--;
}
while(fast != null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
求链表的中间节点,链表长度为奇数,返回最中间的值,链表长度为偶数,返回中间两个值的任意一个即可
两个指针,一次一步,一次两步
遇到一个指针遍历链表不能解决问题的,一般采用双指针,指针快慢速度不同。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode fast = head;
ListNode slow = null;
while(fast != null){
ListNode tmp = fast.next;
fast.next = slow;
slow = fast;
fast = tmp;
}
return slow;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//鲁棒性检测:
//1.都是空指针
ListNode res = new ListNode(0);
ListNode tmp = res;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
tmp.next = l1;
l1 = l1.next;
}else{
tmp.next = l2;
l2 = l2.next;
}
tmp = tmp.next;
}
//2.一个空,一个不空
//三元运算符,满足要求冒号前的结果,不满足,冒号后的结果
tmp.next = l1 == null? l2:l1;
return res.next;
}
}
两步走:第一步在树A中寻找与树B根节点值相同的节点,第二步,判断该节点处向下的树的左右子树与B是否相同
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null)
return false;
return dfs(A, B) || isSubStructure(A.left,B) || isSubStructure(A.right, B);
}
private boolean dfs(TreeNode A, TreeNode B){
if(B == null)
return true;
if(A == null)
return false;
return A.val == B.val && dfs(A.left,B.left) && dfs(A.right,B.right);
}
}