一、鏈表相關
1.鏈表反轉
class Solution { public ListNode reverseList(ListNode head) { // base case if(head == null || head.next == null) return head; ListNode first = head; ListNode result = null;//建立一個新的節點用來存放結果 ListNode second = null; while(first != null){ //遍歷輸入鏈表,開始處理每一個節點 second = first.next; //先處理第一個節點first,所以需要一個指針來存儲first的后繼 first.next = result; //將first放到新鏈表頭節點的頭部 result = first; //移動新鏈表的頭指針,讓它始終指向新鏈表頭部 first = second; //繼續處理原鏈表的節點,即之前指針存放的后繼,循環往復 } return result; } }
3.判斷鏈表是否有環
/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) {//設置快慢指針,若有環一定會在環里面相遇。否則會有null值 if(head==null) return false; ListNode walker = head; ListNode runner = head; while(runner.next!=null && runner.next.next!=null) { walker = walker.next; runner = runner.next.next; if(walker==runner) return true; } return false; } }
//也可以采用hashmap把每一個節點存儲起來,如果地址相同則存在節點
二、數組、字符串巧妙解法相關
1.不使用除法實現除自身外數組元素的乘積(力扣 238)
public class Solution { public int[] productExceptSelf(int[] nums) { int n = nums.length; int[] res = new int[n]; res[0] = 1; for (int i = 1; i < n; i++) { res[i] = res[i - 1] * nums[i - 1]; } int right = 1; for (int i = n - 1; i >= 0; i--) { res[i] *= right; right *= nums[i]; } return res; } }
二、樹相關
1.給定一顆二叉搜索樹,返回該二叉搜索樹第K大的節點
//思路:二叉搜索樹按照中序遍歷的順序打印出來正好就是排序好的順序。 // 所以,按照中序遍歷順序找到第k個結點就是結果。 public class Solution { int index = 0; //計數器 TreeNode KthNode(TreeNode root, int k) { if(root != null){ //中序遍歷尋找第k個 TreeNode node = KthNode(root.left,k); if(node != null) return node; index ++; if(index == k) return root; node = KthNode(root.right,k); if(node != null) return node; } return null; } }
2.二叉樹最小深度
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ //為什么采用分治的思想而不是求數最大深度思想,因為可能為單鏈表(鏈表是特殊樹)沒法處理 class Solution { public int minDepth(TreeNode root) { if(root == null) return 0; int left = minDepth(root.left); int right = minDepth(root.right); return (left == 0 || right == 0) ? left + right + 1//為什么可以寫成left + right + 1 : Math.min(left,right) + 1; //因為left 跟right 必有一個為0,所以.. } }
3.二叉樹最大深度
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public int maxDepth(TreeNode root) { if(root == null) { return 0; } else{ return 1+ Math.max(maxDepth(root.left), maxDepth(root.right)); } } }
三、動態規划相關
1.兩個字符串的最長公共子序列長度
class Solution {
//dp[i][j]表示 0-(i-1) 0-(j-1)之間最長子序列 public int longestCommonSubsequence(String s1, String s2) { int[][] dp = new int[s1.length() + 1][s2.length() + 1]; for (int i = 0; i < s1.length(); ++i) for (int j = 0; j < s2.length(); ++j) if (s1.charAt(i) == s2.charAt(j)) dp[i + 1][j + 1] = 1 + dp[i][j]; else dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); return dp[s1.length()][s2.length()]; } }
2.求連續子數組的最大和
class Solution { public static int maxSubArray(int[] A) { int maxSoFar=A[0], maxEndingHere=A[0]; for(int i=1;i<A.length;++i){ maxEndingHere = Math.max(A[i], maxEndingHere+A[i]); maxSoFar = Math.max(maxEndingHere, maxSoFar); } return maxSoFar; } }
四、排序
1.快排
public static void quickSort(int[] list, int left, int right) { if (left < right) { // 分割數組,找到分割點 int point = partition(list, left, right); // 遞歸調用,對左子數組進行快速排序 quickSort(list, left, point - 1); // 遞歸調用,對右子數組進行快速排序 quickSort(list, point + 1, right); } } /** * 分割數組,找到分割點 */ public static int partition(int[] list, int left, int right) { // 用數組的第一個元素作為基准數 int first = list[left]; while (left < right) { while (left < right && list[right] >= first) { right--; } // 交換 swap(list, left, right); while (left < right && list[left] <= first) { left++; } // 交換 swap(list, left, right); } // 返回分割點所在的位置 return left; } /** * 交換數組中兩個位置的元素 */ public static void swap(int[] list, int left, int right) { int temp; if (list != null && list.length > 0) { temp = list[left]; list[left] = list[right]; list[right] = temp; } }
2.二路歸並排序
public class MergeSort { /** * 歸並排序(Merge Sort)與快速排序思想類似:將待排序數據分成兩部分,繼續將兩個子部分進行遞歸的歸並排序;然后將已經有序的兩個子部分進行合並,最終完成排序。 * 其時間復雜度與快速排序均為O(nlogn),但是歸並排序除了遞歸調用間接使用了輔助空間棧,還需要額外的O(n)空間進行臨時存儲。從此角度歸並排序略遜於快速排序,但是歸並排序是一種穩定的排序算法,快速排序則不然。 * 所謂穩定排序,表示對於具有相同值的多個元素,其間的先后順序保持不變。對於基本數據類型而言,一個排序算法是否穩定,影響很小,但是對於結構體數組,穩定排序就十分重要。例如對於student結構體按照關鍵字score進行非降序排序: */ public static void main(String[] args) { int[] list = {50, 10, 90, 30, 70}; System.out.println("************歸並排序************"); System.out.println("排序前:"); display(list); System.out.println("排序后:"); mergeSort(list, new int[list.length], 0, list.length - 1); display(list); } /** * 歸並排序算法 * @param list 待排序的列表 * @param tempList 臨時列表 * @param head 列表開始位置 * @param rear 列表結束位置 */ public static void mergeSort(int[] list, int[] tempList, int head, int rear) { if (head < rear) { // 取分割位置 int middle = (head + rear) / 2; // 遞歸划分列表的左序列 mergeSort(list, tempList, head, middle); // 遞歸划分列表的右序列 mergeSort(list, tempList, middle + 1, rear); // 列表的合並操作 merge(list, tempList, head, middle + 1, rear); } } /** * 合並操作(列表的兩兩合並) * @param list * @param tempList * @param head * @param middle * @param rear */ public static void merge(int[] list, int[] tempList, int head, int middle, int rear) { // 左指針尾 int headEnd = middle - 1; // 右指針頭 int rearStart = middle; // 臨時列表的下標 int tempIndex = head; // 列表合並后的長度 int tempLength = rear - head + 1; // 先循環兩個區間段都沒有結束的情況 while ((headEnd >= head) && (rearStart <= rear)) { // 如果發現右序列大,則將此數放入臨時列表 if (list[head] < list[rearStart]) { tempList[tempIndex++] = list[head++]; } else { tempList[tempIndex++] = list[rearStart++]; } } // 判斷左序列是否結束 while (head <= headEnd) { tempList[tempIndex++] = list[head++]; } // 判斷右序列是否結束 while (rearStart <= rear) { tempList[tempIndex++] = list[rearStart++]; } // 交換數據 for (int i = 0; i < tempLength; i++) { list[rear] = tempList[rear]; rear--; } } /** * 遍歷打印 */ public static void display(int[] list) { if (list != null && list.length > 0) { for (int num :list) { System.out.print(num + " "); } System.out.println(""); } } }
五、LRU LFU
1.lru采用雙鏈表+hashmap實現
class LRUCache { int size; Node head,tail; HashMap<Integer,Node> hm; public LRUCache(int capacity) { size=capacity; head=tail=null; hm=new HashMap<>(); } public int get(int key) { if(!hm.containsKey(key)) { return -1; } else { Node ref=hm.get(key); if(ref==head) return ref.val; if(ref!=head && ref!=tail) { ref.Llink.Rlink=ref.Rlink; ref.Rlink.Llink=ref.Llink; } else if(ref!=head && ref==tail) { tail=tail.Llink; tail.Rlink=null; } ref.Llink=null; ref.Rlink=head; head.Llink=ref; head=ref; return ref.val; } } public void put(int key, int value) { if(hm.containsKey(key)) { hm.get(key).val=value; this.get(key); } else { Node temp=new Node(value,key); if(hm.size()<size) { hm.put(key,temp); if(head==null) { head=temp; tail=temp; } else { temp.Rlink=head; head.Llink=temp; head=temp; } } else { if(tail==head) { hm.remove(tail.key); hm.put(key,temp); head=tail=temp; } else { hm.remove(tail.key); hm.put(key,temp); Node help=tail; tail=tail.Llink; tail.Rlink=null; help.Llink=null; temp.Rlink=head; head.Llink=temp; head=temp; } } } } } class Node{ int val; int key; Node Rlink; Node Llink; Node(int val,int key) { this.key=key; this.val=val; Rlink=null; Llink=null; } }
2.lfu