字节跳动

链表翻转、插入、删除

反转

class Solution {
    public ListNode reverseList(ListNode head) {
         if(head==null||head.next==null){
             return head;
         }
         ListNode cur=head;
         ListNode reversehead=null;
         ListNode temp=null;
         while(cur!=null){
             temp=cur.next;
             cur.next=reversehead;
             reversehead=cur;
             cur=temp;
         }
         return reversehead;
    }
}

删除链表节点

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
           ListNode dummyhead=new ListNode(0);//采用了虚拟头节点
           dummyhead.next=head;
           ListNode cur=head;
           ListNode pre=dummyhead;
           while(cur!=null){
               if(cur.val==val){
                   pre.next=cur.next;
                   break;
               }
               pre=cur;
               cur=cur.next;
           }
           return dummyhead.next;
    }
}

链表中倒数第K个结点

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(k==0||head==null){
            return  null;
        }
        ListNode first=head;
        ListNode second=head;

        for(int i=0;i<k-1;i++){
            second=second.next;//比如要找倒数第二个,则先划定范围在两个
            if(second==null){
                return null;
            }
        }
        while(second.next!=null){//然后再往后移动,直到second为最后一个
            first=first.next;
            second=second.next;
        }
        return first;
    }
}

合并排序链表

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
       if (l1== null && l2 == null) { //如果两个链表都为空
           return null;
        }
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
           return l1;
        }
    ListNode head;//新链表的头节点
    ListNode cur;//用来比较的中间节点

    //初始化,将head和cur都指向两个链表中最小的节点
    if(l1.val<l2.val){
        head=l1;
        cur=l1;
        l1=l1.next;
    }
    else{
        head=l2;
        cur=l2;
        l2=l2.next;
    }

    //比较,小的加入链表,直到其中一条链表为空
    while(l1!=null&&l2!=null){
        if(l1.val<l2.val){
            cur.next=l1;
            cur=cur.next;
            l1=l1.next;
        }
        else{
            cur.next=l2;
            cur=cur.next;
            l2=l2.next;
        }
    }

    while(l1!=null){
        cur.next=l1;
        cur=cur.next;
        l1=l1.next;
    }

    while(l2!=null){
        cur.next=l2;
        cur=cur.next;
        l2=l2.next;
    }
    
    return head;
    }
}

从尾到头打印链表

public class Solution {
    public int[] reversePrint(ListNode head) {
        ArrayList<Integer> list = new ArrayList<>();
        while (head != null) {
            list.add(head.val);
            head = head.next;
        }
        int size = list.size();
        int[] array = new int[size];
        for (int i = 0; i < size; i++) {
            array[i] = list.get(size - i -1);
        }
        return array;
    }
}

回文链表
输入: 1->2->2->1
输出: true

class Solution {
    public boolean isPalindrome(ListNode head) {
        ListNode reversed=reverseAndClone(head);
        return isEqual(head,reversed);
    }

     public ListNode reverseAndClone(ListNode node){
         ListNode head = null;
        while(node != null) {
            ListNode n = new ListNode(node.val); //复制
            n.next = head;
            head = n;
            node = node.next;
        }
        return head;
    }
     

     public boolean isEqual(ListNode node1,ListNode node2){
         while(node1!=null&&node2!=null){
             if(node1.val!=node2.val){
                 return false;
             }
             node1=node1.next;
             node2=node2.next;
         }
         return node1==null&&node2==null;
     }
}

K个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
在这里插入图片描述

在这里插入图片描述

    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode tail = head;
        for (int i = 0; i < k; i++) {
            //剩余数量小于k的话,则不需要反转。
            if (tail == null) {
                return head;
            }
            tail = tail.next;
        }
        // 反转前 k 个元素
        ListNode newHead = reverse(head, tail);
        //下一轮的开始的地方就是tail
        head.next = reverseKGroup(tail, k);

        return newHead;
    }

    /*
    左闭右开区间
     */
    private ListNode reverse(ListNode head, ListNode tail) {
        ListNode pre = null;
        ListNode next = null;
        while (head != tail) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;

    }

在 O(n log n) 时间复杂度和常数级的空间复杂度下给链表排序
归并排序

public ListNode sortList(ListNode head) {
		//  0个节点 ||  1个节点
		if (head == null || head.next == null)  
			return head;
		//  >= 2个节点
		ListNode first = head, second = null, mid = getMid(head);
		second = mid.next;
		mid.next = null;  //将链表分为两段!!!!
		//递归
		first = sortList(first);
		second = sortList(second);
		return merge(first, second);
	}
 
	//排序
	ListNode merge(ListNode first, ListNode second) {
		if (first == null)
			return second;
		if (second == null)
			return first;
		
		ListNode res = new ListNode(0);
		ListNode curr = res;//控制新链表顺序的point
 
		while (first != null && second != null) {
			if (first.val < second.val) {
				curr.next = first;
				curr = curr.next;
				first = first.next;
			} else {
				curr.next = second;
				curr = curr.next;
				second = second.next;
			}
		}
 
		if (first != null)
			curr.next = first;
		if (second != null)
			curr.next = second;
		
		return res.next;
	}
	
	//将链表平分为两段,返回第一段末尾   例如:5个点返回2号点,6个点返回3号点
	ListNode getMid(ListNode head) {
		ListNode slow = head, fast = head.next;
		
		while (fast!=null&&fast.next!=null) {
			slow=slow.next;
			fast=fast.next.next;
		}
		
		return slow;
	}

字符串

在给定字符串情况下,求最长的不含重复字符的字串的长度。

class Solution {
    public int lengthOfLongestSubstring(String s) {
          int head=0;
          int tail=0;
          if(s.length()<2){
              return s.length();
          }
          int res=1;
          while(tail<s.length()-1){
              tail++;
              if(!s.substring(head,tail).contains(s.substring(tail,tail+1))){//如果当前元素之前的不重复结果集没包含该元素,则包含进来
                  res=Math.max(tail-head+1,res);
              }
              else{
                  while(s.substring(head,tail).contains(s.substring(tail,tail+1))){//包含该元素,头节点往后移
                      head++;
                  }
              }
          }
          return res;
    }
}

最长公共子序列
一,定要明确 dp 数组的含义
对于两个字符串的动态规划问题,套路是通用的
比如说对于字符串 s1 和 s2,它们的长度分别是 m、n,一般来说都要构造一个这样的 DP table:int[][] dp = new int[m+1][n+1]。

这里为什么要加1,原因是你可以不加1,但是不加1你就会用其它限制条件来确保这个index是有效的,而当你加1之后你就不需要去判断只是让索引为0的行和列表示空串。

第二步,定义 base case
我们专门让索引为0的行和列表示空串,dp[0][…] 和 dp[…][0] 都应该初始化为0,这就是base case。

第三步,找状态转移方程
text1 = “abcde”, text2 = “ace”
最长公共子序列是 “ace”,它的长度为 3。
在这里插入图片描述
第一次遍历 i = 1, j = 1,两个a相同所以 dp[1][1] = 1
第二次遍历 i = 1, j = 2,a与c不等,也不能是0,这里需转换成 a 与 ac 最长子序列,这里需要把之前的关系传递过来,所以dp[1][2] = 1
第三次遍历 i = 1, j = 3,a与e不相同,把之前的关系传递过来,所以dp[1][3] = 1

因此可以得出:
现在对比的这两个字符不相同的,那么我们要取它的「要么是text1往前退一格,要么是text2往前退一格,两个的最大值」
dp[i + 1][j + 1] = Math.max(dp[i+1][j], dp[i][j+1]);

对比的两个字符相同,去找它们前面各退一格的值加1即可:dp[i+1][j+1] = dp[i][j] + 1;

// java
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] dp = new int[m + 1][n + 1];

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 获取两个串字符
                char c1 = text1.charAt(i), c2 = text2.charAt(j);
                if (c1 == c2) {
                    // 去找它们前面各退一格的值加1即可
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                } else {
                    //要么是text1往前退一格,要么是text2往前退一格,两个的最大值
                    dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]);
                }
            }
        }
        return dp[m][n];
    }
}

编辑距离:
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符

输入:word1 = “horse”, word2 = “ros”
输出:3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)
在这里插入图片描述
.1. dp[i][j] 代表 word1 中前 i 个字符,变换到 word2 中前 j 个字符,最短需要操作的次数
2.需要考虑 word1 或 word2 一个字母都没有,即全增加/删除的情况,所以预留 dp[0][j] 和 dp[i][0]
3.状态转移
31. 增,dp[i][j] = dp[i][j - 1] + 1
32. 删,dp[i][j] = dp[i - 1][j] + 1
33. 改,dp[i][j] = dp[i - 1][j - 1] + 1
3.4按顺序计算,当计算 dp[i][j] 时,dp[i - 1][j] , dp[i][j - 1] , dp[i - 1][j - 1] 均已经确定了
3.5配合增删改这三种操作,需要对应的 dp 把操作次数加一,取三种的最小
3.6如果刚好这两个字母相同 word1[i - 1] = word2[j - 1] ,那么可以直接参考 dp[i - 1][j - 1] ,操作不用加一

class Solution {
    public int minDistance(String word1, String word2) {
        int len1 = word1.length();
        int len2 = word2.length();

        int[][] dp = new int[len1 + 1][len2 + 1];

        // 插入操作
        for(int i = 1; i <= len1; i++) {
            dp[i][0] = dp[i - 1][0] + 1;
        }

        // 删除操作
        for(int j = 1;j <= len2; j++) {
            dp[0][j] = dp[0][j - 1] + 1;
        }

        for(int i = 1; i <= len1; i++) {
            for(int j = 1; j <= len2; j++) {
                // 删除操作:dp[i - 1][j]
                // 增加操作:dp[i][j - 1]
                // 替换操作:dp[i - 1][j - 1]
                dp[i][j] = Math.min(dp[i - 1][j], Math.min(dp[i - 1][j - 1], dp[i][j - 1])) + 1;//取三种最小

                if(word1.charAt(i - 1) == word2.charAt(j - 1)) {//表示i,j位置字母相同
                    dp[i][j] =dp[i - 1][j - 1];
                }
            }
        }
        return dp[len1][len2];
    }
}

二叉树

1.二叉树前序遍历(递归和非递归)
递归:

public void qianxu(node t)// 前序递归 前序遍历:根结点 ---> 左子树 ---> 右子树
{
	if (t != null) {
		System.out.print(t.value + " ");// 当前节点
		qianxu(t.left);
		qianxu(t.right);
	}
}

非递归:

在这里插入图片描述
每pop完添加右左节点直接输出(访问)即可完成前序非递归遍历。

public void qianxu3(node t)// 非递归前序 栈 先左后右  t一般为root
{
	Stack<node> q1 = new Stack<node>();
	if (t == null)
		return;
	if (t != null) {
		q1.push(t);
	}
	while (!q1.empty()) {
		node t1 = q1.pop();
		if (t1.right != null) {
			q1.push(t1.right);
		}
		if (t1.left != null) {
			q1.push(t1.left);
		}
		System.out.print(t1.value + " ");
	}
}

2.二叉树中序遍历(递归和非递归)
递归:

public void zhongxu(node t)// 中序遍历 中序遍历:左子树---> 根结点 ---> 右子树
{
	if (t != null) {
		zhongxu(t.left);
		System.out.print(t.value + " ");// 访问完左节点访问当前节点
		zhongxu(t.right);
	}
}

非递归:
在这里插入图片描述

public void zhongxu2(node t) {
	Stack<node> q1 = new Stack();	
	while(!q1.isEmpty()||t!=null)
	{
		if (t!=null) {
			q1.push(t);
			t=t.left;
		}
		else {
			t=q1.pop();
			System.out.print(t.value+" ");
			t=t.right;
		}
	}
}

3.二叉树后序遍历(递归和非递归)
递归:

public void houxu(node t)// 后序遍历 后序遍历:左子树 ---> 右子树 ---> 根结点
{
	if (t != null) {
		houxu(t.left);
		houxu(t.right);
		System.out.print(t.value + " "); // 访问玩左右访问当前节点
	}
}

非递归:
在这里插入图片描述

public void houxu2(node t) {
	Stack<node> q1 = new Stack();	
	Map<Integer,Integer >map=new HashMap<>();
	while(!q1.isEmpty()||t!=null)
	{
		if (t!=null) {
			q1.push(t);
			map.put(t.value, 1); //t.value标记这个值节点出现的次数
			t=t.left;
		}
		else {
			t=q1.peek();
			if(map.get(t.value)==2) {//第二次访问,抛出
				q1.pop();
				System.out.print(t.value+" ");
				t=null;//需要往上走
			}
			else {
				map.put(t.value, 2);
				t=t.right;
			}
			
		}
	}
}

4.二叉树查找的最短搜索路径

public class TreeNode {
    int val=0;
    TreeNode left=null;
    TreeNode right=null;

    public TreeNode(int val) {
        this.val = val;
    }
}
public class Solution2 {
    private ArrayList<ArrayList<Integer>> allPaths=new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> onePath=new ArrayList<Integer>();

    public ArrayList<ArrayList<Integer>> findAllPath(TreeNode root) {
        if(root==null)
            return allPaths;
        onePath.add(root.val);
        //若为叶子节点,则onePath加入到allPaths;
        if(root.left==null&&root.right==null){
             allPaths.add(new ArrayList<Integer>(onePath));
        }
        findAllPath(root.left);
        findAllPath(root.right);
        onePath.remove(onePath.size()-1);
        return allPaths;
    }
}

数组

1.积水问题
在这里插入图片描述

public static void main(String[] args) {
          int [] a={0,1,0,2,1,0,1,3,2,1,2,1};
          int  b=getwater2(a);
          System.out.print(b);
    }
private static int getwater2(int[] orgin){
          int size=orgin.length;
          if(size<3){
              return 0;
          }
          System.out.println(Arrays.toString(orgin));
          int[] water= new int[size];
          int maxNum=0;
          for(int i=0;i<size;i++){//第一次遍历得到最高台阶高度
              if(orgin[i]>maxNum){
                  maxNum=orgin[i];
              }
          }
          for(int i=0;i<size;i++){//初始化积水统计数组
              water[i]=maxNum-orgin[i];
          }
          int left=orgin[0];
          if(water[0]>0){//不是开头最高
              water[0]=0;
              for(int j=1;j<size&&orgin[j]<maxNum;j++){
                  if(orgin[j]>=left){//大于左边元素,left指针移到该元素位置,并让该位置的water数组值为0
                      left=orgin[j];
                      water[j]=0;
                  }else{//否则让该位置water元素值为left-orgin[j]
                      water[j]=left-orgin[j];
                  }
              }
          }
          int right=orgin[size-1];
          if(water[size-1]>0){//不是结尾最高
              water[size-1]=0;
              for(int j=(size-2);j<size&&orgin[j]<maxNum;j--){
                  if(orgin[j]>=right){
                      right=orgin[j];
                      water[j]=0;
                  }else{
                      water[j]=right-orgin[j];
                  }
              }
          }
           int sum=0;
            for(int i=0;i<size;i++){
                sum+=water[i];
            }
            return sum;
      }

2.两个排序的数组A和B分别含有m和n个数,找到两个排序数组的中位数,要求时间复杂度应为O(log (m+n))。
给出数组A = [1,2,3,4,5,6] B = [2,3,4,5],中位数3.5
给出数组A = [1,2,3] B = [4,5],中位数 3

class Solution {
    /**
     * @param A: An integer array.
     * @param B: An integer array.
     * @return: a double whose format is *.5 or *.0
     */
    public double findMedianSortedArrays(int[] A, int[] B) {
        if (A == null && B == null) return 0;
        int len = A.length + B.length;
        if (len % 2 == 0){
            return (findKth(A, 0, B, 0, len/2) + findKth(A, 0, B, 0, len/2 + 1)) / 2.0;
        }
        return findKth(A, 0, B, 0, len/2+1);
    }
    public static double findKth(int[] A, int startA, int[] B, int startB, int k){
        if (startA >= A.length){
            return B[startB + k - 1];
        }
        if (startB >= B.length){
            return A[startA + k - 1];
        }
        if (k == 1){
            return Math.min(A[startA], B[startB]);
        }
        int keyA = startA + k / 2 - 1 < A.length ? A[startA + k / 2 - 1] : Integer.MAX_VALUE;
        int keyB = startB + k / 2 - 1 < B.length ? B[startB + k / 2 - 1] : Integer.MAX_VALUE;
        if (keyA < keyB){
            return findKth(A, startA + k / 2, B, startB, k - k / 2);
        }
        return findKth(A, startA, B, startB + k / 2, k - k / 2);
    }

}

1.数组中第k大的数-最小堆
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2 输出: 5

    public int findKthLargest(int[] nums, int k) {
        final PriorityQueue<Integer> queue = new PriorityQueue<>();//PriorityQueue默认是最小堆,即孩子节点比父亲节点大,则堆顶是最小的,所以我们维护一个容量大小为K的堆,当大小超过K时,则删除堆顶元素,最后的堆顶元素就是第K大的元素了
        for (int val : nums) {
            queue.add(val);
            if (queue.size() > k)
                queue.poll();
        }
        return queue.peek();
    }

时间复杂度为O(N*logk),因为二叉堆的插入和删除操作都是logk的时间复杂度。上述代码是“不处理重复数据方法”.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值