π It is necessary to consider ideas and boundary conditions
πΏπ₯ The mark indicates that the question type is a little out of line
data structure
array
Duplicate number in array
Title: for a number with length N, all numbers in the group are in the range of 0~N-1. Some numbers in the array are repeated, but I don't know how many numbers are repeated or how many times each number is repeated. Please find any repeated number in the array as the return value.
- Make full use of the corresponding relationship between subscript and value
- It is similar to bucket sorting
public class Main { /** * @param arr target array * Time complexity: O(N) * Space complexity: O(1) */ public static Integer repetition(int[] arr) { for (int i = 0; i < arr.length; i++) { while (arr[i] != i) { // If there is no such return statement, the loop may not exit // This condition means that there is already a value with arr[i] as the subscript value and arr[i]. At this time, two arr[i] appear if (arr[arr[i]] == arr[i]) { return arr[i]; } // Swap values with subscript i and subscript arr[i] at this time int temp = arr[i]; arr[i] = arr[temp]; arr[temp] = temp; } } return null; } }
Think: output missing and duplicate elements in the array.
public class Main { // Stores repeated elements in an array public static HashSet<Integer> repetitionHashSet = new HashSet<>(); // Stores the missing elements between 0 and N-1 in the array public static ArrayList<Integer> restNumList = new ArrayList<>(); public static void repetition(int[] arr) { for (int i = 0; i < arr.length; i++) { modify(arr, i); } // At this time, all elements should be in the corresponding position unless there is no corresponding value // Where it does not exist, there will be a value that duplicates other values for (int i = 0; i < arr.length; i++) { if (arr[i] != i) { repetitionHashSet.add(arr[i]); restNumList.add(i); } } } private static void modify(int[] arr, int index) { // First, ensure that the arr[index] and the corresponding value are not index // In addition, the element with arr[index] as the subscript cannot already be in the correct position to prevent it from entering the dead loop // Each run only changes the element position, not the element value in the arr while (arr[index] != index && !(arr[arr[index]] == arr[index])) { // Exchange two values with index and arr[index] int temp = arr[index]; arr[index] = arr[temp]; arr[temp] = temp; } } }
Do not modify the array to find duplicate elements
Problem: all numbers in the array with length N+1 are in the range of 1~N, so at least one number in the array is duplicate. Please find any duplicate number in the array, but you cannot modify the input array. Required space complexity O (1)
- Dichotomy classifies the data and judges the left and right positions of duplicate data in the case of dichotomy.
- If the number of data is greater than the number of integers in the range, there must be duplicate values in the range
public class Main { /** * @param arr target array * @return duplicate value * Time complexity O(NlogN) * Space complexity O(1) */ public static Integer repetition(int[] arr) { if (arr == null || arr.length == 0) { return null; } int left = 1; int right = arr.length - 1; // When left==right, we do not know whether it is duplicate data, so we need to make a countRange method call to judge. while (left <= right) { int med = ((right - left) >> 1) + left; // Calculate the number of data between left..med int count = countRange(arr, left, med); if (left == right) { if (count > 1) { return left; } else { break; } } // If the number of data is more than the integer number between left...med, there must be duplicate values // Set the range of duplicate values according to the number of data, so as to narrow the range of duplicate data if (count > (med - left + 1)) { right = med; } else { left = med + 1; } } return -1; } private static int countRange(int[] arr, int left, int med) { int count = 0; for (int i = 0; i < arr.length; i++) { if (left <= arr[i] && arr[i] <= med) { ++count; } } return count; } }
Find in 2D array
Title: in a two-dimensional array, each row is sorted in ascending order from left to right, and each column is sorted in ascending order from top to bottom. Complete a function, input such a two-dimensional array and an integer, and judge whether there are such integers in the array.
- Data search is performed in the range near the value of the target value in the arr
public class Main { public static boolean process(int[][] arr, int x) { // Abnormal condition if (arr == null || arr.length == 0 || arr[0].length == 0 || arr[0][0] > x || arr[arr.length - 1][arr[0].length - 1] < x) { return false; } int row = 0; int line = 0; // Control range while (row >= 0 && row < arr.length && line >= 0 && line < arr[0].length) { // If the target value is larger than arrow [row] [line], there are two directions: right and down if (arr[row][line] < x) { // Are you satisfied? if (line + 1 < arr[0].length && arr[row][line + 1] < x) { ++line; } else { ++row; } // The target value is smaller than arrow [row] [line] } else if (arr[row][line] > x) { --line; } else {// Equal to target value return true; } } // Return does not exist because the range is out of bounds return false; } }
character string
Replace spaces
Title: please implement a function to convert each space in the string to% 20
- If it is from left to right, the time complexity O (N ²)οΌ The main reason for the waste of time is the failure to fill in the characters directly where they should be.
- If you want to be able to put it directly in the corresponding position, you need to know how many spaces there are.
- And in the process of convenient expansion, it must be from right to left to prevent data coverage.
public class Main { public static void main(String[] args) { char[] arr = new char[100]; System.out.println(arr.length); arr[0] = ' '; arr[1] = 'a'; arr[2] = ' '; arr[3] = 'a'; arr[4] = 'a'; arr[5] = 'a'; arr[6] = ' '; arr[7] = 'a'; arr[8] = 'a'; arr[9] = ' '; process(arr, 10, 100); } /** * @param arr Array object * @param thisLen Current array length * @param maxLen arr Maximum capacity * Time complexity O(N) * Space complexity O(1) */ public static void process(char[] arr, int thisLen, int maxLen) { if (arr == null || maxLen <= 0 || thisLen <= 0) { return; } // Count the number of spaces int sumK = 0; for (int i = 0; i < thisLen; i++) { if (arr[i] == ' ') { ++sumK; } } // Calculate new array length int newLen = sumK * 2 + thisLen; if (newLen > maxLen) { return; } // Traverse two pointers int index = thisLen - 1; int newIndex = newLen - 1; while (index >= 0) { if (arr[index] == ' ') { arr[newIndex--] = '0'; arr[newIndex--] = '2'; arr[newIndex] = '%'; } else { arr[newIndex] = arr[index]; } --index; --newIndex; } } }
Linked list
Print linked list from end to end
Title: enter the head node of a linked list and print the value of each node from tail to head.
- Implementation of three methods
- The time complexity constant of stack structure and recursive implementation is 1 * O(N), and the reverse implementation is 3 * O(N)
- Stack structure and recursive space complexity O(N), and reverse order O(1)
- This is a typical case of space for time.
public class Main { //Stack implementation public static void process_1(Node header) { if (header == null) { return; } Stack<Node> stack = new Stack<>(); Node tail = header; while (tail != null) { stack.push(tail); tail = tail.nextNode; } while (!stack.isEmpty()) { System.out.println(stack.pop().num); } } // Recursive implementation public static void process_2(Node header) { if (header == null) { return; } process_2(header.nextNode); System.out.println(header.num); } // Implementation of reverse order linked list public static void process_3(Node header) { if (header == null) { return; } Node pre = null; Node tail = header; Node post = null; while (tail != null) { post = tail.nextNode; tail.nextNode = pre; pre = tail; tail = post; } header = pre; while (pre != null) { System.out.println(pre.num); pre = pre.nextNode; } pre = null; tail = header; post = null; while (tail != null) { post = tail.nextNode; tail.nextNode = pre; pre = tail; tail = post; } } public static class Node { int num; Node nextNode; public Node(int num, Node nextNode) { this.num = num; this.nextNode = nextNode; } } }
tree
Rebuild binary tree π
Title: enter the results of preorder traversal and inorder traversal of a binary tree, please rebuild the binary tree. Assuming that the input precursor and subsequent nodes have no duplicate numbers, the function returns the root node of the tree.
- Note that we judge the number of the left and right subtrees of the root node according to the position of the first value in the preorder traversal.
- Pay attention to judge whether the left and right subtrees exist, otherwise an ArrayIndexOutOfBoundsException error will appear in the range. Both methods can be used.
- This is a large range of data into a small range, that is, a large problem is transformed into a small problem with the same essence, so it is usually implemented recursively.
public class Main { public static Node construction(int[] pre, int[] in) { if (pre == null || in == null || pre.length == 0 || in.length == 0 || pre.length != in.length) { return null; } return construction(pre, 0, pre.length - 1, in, 0, in.length - 1); } /** * @param pre Preorder traversal * @param preStart The first element of the preorder traversal part * @param preEnd Tail element of preorder traversal part * @param in Medium order traversal * @param inStart The first element of the traversal part in the middle order * @param inEnd Tail element of traversal part in middle order * @return pre[preStart]Corresponding current node * Note: the elements contained in pre[preStart...preEnd] and in[inStart...inEnd] are identical */ private static Node construction(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd) { // Create the current node pre[preStart] Node root = new Node(pre[preStart]); if (preStart == preEnd) { return root; } // Find the data location in in according to pre[preStart] and record it as data A, // The number of elements in front of A is recorded with leftNum int leftNum = 0; int tail = inStart; while (pre[preStart] != in[tail]) { ++leftNum; ++tail; } /* * In fact, a judgment is added at the beginning of the function * if(preStart>preEnd)return null; * You can also avoid the absence of the left and right subtrees of root, but this way is better understood * * In addition, observe the range when the left and right subtrees are established: * Preorder traversal: * pre[preStart]Is the current root node * pre[preStart+1...preStart + leftNum]Left subtree range * pre[preStart + leftNum + 1...preEnd]Right subtree range * Middle order traversal: * in[inStart...inStart + leftNum - 1]Left subtree range * in[inStart + leftNum]Is the current root node * in[inStart + leftNum + 1...inEnd]Right subtree range */ // If leftnum > 1, it indicates that the current root has a left subtree if (leftNum > 0) { root.leftNode = construction(pre, preStart + 1, preStart + leftNum, in, inStart, inStart + leftNum - 1); } // Determine whether there is a right subtree if (leftNum < preEnd - preStart) { root.rightNode = construction(pre, preStart + leftNum + 1, preEnd, in, inStart + leftNum + 1, inEnd); } return root; } public static class Node { int num; Node leftNode; Node rightNode; public Node(int num) { this.num = num; } } }
Next node of binary tree
Topic: given a binary tree and other nodes, how to find the next node traversed in middle order? In addition, the node type constituting the tree records the parent node of the node.
- The target node has a right subtree, which is the leftmost element in the right subtree (circular left search).
- The target node does not have A right subtree, that is, A node in the ancestor node is recorded as (A), where it is determined that the target node is an element in the left subtree of the ancestor node A (cyclic upward search).
public class Main { public static Node nextNode(Node node) { if (node == null) { return null; } // If the node has a right node if (node.rightNode != null) { return leftestNode(node.rightNode); } // If there is no right node, the next element must be A parent node and A in the above node // And the node node is set in the left subtree of A, otherwise it does not exist return leftChildOfParent(node); } // Find in the left subtree of that node private static Node leftChildOfParent(Node node) { Node parent = node.parentNode; while (parent != null && parent.rightNode == node) { node = parent; parent = parent.parentNode; } return parent; } // Find leftmost node private static Node leftestNode(Node rightNode) { Node tail = rightNode; while (tail.leftNode != null) { tail = tail.leftNode; } return tail; } public static class Node { int num; Node leftNode; Node rightNode; Node parentNode; public Node(int num) { this.num = num; } } }
Stack and queue
Dual stack implementation queue
Title: use two stack structures to complete the function of queue and realize two methods: appendTail: add elements and deleteHeader: delete elements.
- Stack structure can be understood as the ability to reverse the order of data. Then two reverse orders are positive orders, so two stack structures can realize the queue structure.
public class Main { Stack<Integer> addStack; Stack<Integer> deleteStack; public Main() { this.addStack = new Stack<Integer>(); this.deleteStack = new Stack<Integer>(); } public void appendTail(int num) { addStack.push(num); } public Integer deleteHeader() { //You must delete the elements in deleteStack before you can take the elements from addSrtack to deleteStack if (!deleteStack.isEmpty()) { return deleteStack.pop(); } if (addStack.isEmpty()) { return null; } //Every time you delete from addStack to deleteStack, the elements in addStack will be deleted while (!addStack.isEmpty()) { deleteStack.add(addStack.pop()); } return deleteStack.pop(); } }
Dual queue implementation stack
Title: use two queue structures to complete the function of stack and realize two methods: add: add elements and poll: delete elements.
- When adding, it is only added in the queue with elements. If it is empty, it is optional.
- When deleting, use the queue to reverse the elements back and forth, and delete the last remaining element.
- Note: keep one of the queues empty after each addition or deletion.
public class Main { private ArrayDeque<Integer> deque1; private ArrayDeque<Integer> deque2; private boolean hasNum_1 = true; private boolean hasNum_2 = false; private boolean flag;//When true, deque1 has elements, and when false, deque2 has elements. public Main() { deque1 = new ArrayDeque<>(); deque2 = new ArrayDeque<>(); flag = hasNum_1; } public void add(int num) { if (flag) { deque1.add(num); }else { deque2.add(num); } } public Integer poll() { if (flag) { if (deque1.isEmpty()) { return null; } while (deque1.size()!=1) { deque2.push(deque1.poll()); } flag=hasNum_2; return deque1.poll(); }else { if (deque2.isEmpty()) { return null; } while (deque2.size()!=1) { deque1.push(deque2.poll()); } flag=hasNum_1; return deque2.poll(); } } }
Algorithms and data manipulation
Recursion and loop
- Recursion: change a large problem into a sub problem for solution
Fibonacci sequence
Question: find the nth Fibonacci number.
- Because recursive calls need to save variables, return parameters and create temporary variables, the overall performance and speed are not as good as loops.
- Loops have the best performance, and recursion has less code.
public class Main { //Recursive implementation private static Integer process(int N) { if (N==1||N==2) { return 1; } return process(N-1)+process(N-2); } //Loop implementation private static Integer process(int N) { if (N<=0) { return null; } if (N<3) { return 1; } int pre=1; int post=1; int res=0; for (int i = 2; i < N; i++) { res=pre+post; pre=post; post=res; } return res; } }
The frog jumped the steps
A frog can jump up one step or two steps at a time. Find out how many jumping methods there are for a frog to jump up N steps.
- There are two possibilities to the nth step, from N-1 or N-2, and then take N-1 and N-2 as target steps.
- Up to 1 and 2 steps, there is one jump method for step 1 and two jump methods for step 2.
- Typical Fibonacci problem
public class Main { private static Integer process(int N) { if (N<=0) { return null; } if (N==1) { return 1; } if (N==2) { return 2; } int pre=1; int post=2; int res=0; for (int i = 2; i < N; i++) { res=pre+post; pre=post; post=res; } return res; } }
Find and sort
- Sequence, binary, hash table, binary tree.
- Although the time complexity of hash table is O(1), additional space complexity is required.
- Under different conditions, different data conditions and different constraints, you can't choose.
- For example, the time complexity of quick sort is close to o (n) in basically sorted data
- Another example: when a small amount of data and space can be opened up, and the compared data is a general data type, bucket sorting is the best choice, and the time complexity is O(N).
Minimum value after rotating array (sorted) π
Title: move the first elements of a sorted array to the end of the array, and output the minimum value of the rotated array, with space complexity O(1). For example, {3, 4, 5, 1, 2, 2}, the minimum value is 1
- If you use the order of O(N), the condition: it is no use if it is already ordered.
- Obviously, when we think of binary search, we change the conditions for narrowing the range of binary search to make the range close to our target array.
- However, we do not know the reduction direction, so we need to carry out linear search.
public class Main { public static Integer process(int[] arr) { if (arr == null || arr.length == 0) { return null; } int tail_1 = 0; int tail_2 = arr.length - 1; // If the conditions are met, it indicates that the whole is ordered, and arr is an ordered array. if (arr[tail_1] < arr[tail_2]) { return arr[0]; } // Because tail_1 points to the left ordered sequence, tail_2 points to the right ordered sequence, and the fixed phase difference > = 1. When the phase difference is 1, tail_2 points to the minimum while (tail_2 - tail_1 != 1) { int med = ((tail_2 - tail_1) >> 1) + tail_1; //If the three values are the same in the subsequent process, the direction of reducing the range cannot be judged, so linear search should be carried out from now on. //Arrays like {3, 1, 3, 3, 3, 3} {3, 3, 3, 3, 1, 3} cannot be determined to the left or right if (arr[tail_1] == arr[tail_2] && arr[tail_1] == arr[med]) { return process(arr, tail_1, tail_2); } // This way of judging and moving range makes tail_1 is always the ordered part in front of arr, tail_2 always the ordered part after arr // Because it has been judged that the three values of arr[tail_1], arr[tail_2] and arr[med] are not the same before the interpretation. // Therefore, during judgment, if arr[med] == arr[tail_1] is true, then arr [Med] > arr [tail_2] is true, so tail_ 1 move to the right // The equal sign must be in tail_1 on the condition of moving to the left. if (arr[med] >= arr[tail_1]) { tail_1 = med; } else if (arr[med] < arr[tail_2]) { tail_2 = med; } } return arr[tail_2]; } private static Integer process(int[] arr, int tail_1, int tail_2) { int min = Integer.MAX_VALUE; // Finds the minimum value in the current range. for (int i = tail_1; i <= tail_2; i++) { min = Math.min(min, arr[i]); } return min; } }
Backtracking method
Path in matrix
Title: please design a function to judge whether there is a path containing all characters of a string in the matrix. The path can start from any position in the matrix, and each part can move up, down, left and right in the matrix. If a path has passed a grid of the matrix, the path cannot enter the grid again.
- Each grid may be the starting node, and there is not much available relationship between each path and other paths, so all grids should be judged as the starting node.
- Due to the direction positioning, recursive retraction is performed like a maze.
public class Main { public static boolean process(char[][] matrix, char[] str) { if (matrix == null || matrix.length == 0 || matrix[0].length == 0 || str.length == 0) { return false; } int rows = matrix.length; int line = matrix[0].length; // Used to record whether the path has been taken boolean[][] isVisited = new boolean[rows][line]; int pathLen = 0; // Make initial judgment for each element for (int i = 0; i < rows; i++) { for (int j = 0; j < line; j++) { if (hasSuccessPath(matrix, i, j, str, 0, isVisited)) { return true; } } } return false; } /** * @param matrix Element matrix * @param row Judge row position elements in matrix * @param line Determine column position elements in matrix * @param str Target string to find * @param index Which element of the target string * @param isVisited Form for recording whether it has passed * @return Is the path feasible */ private static boolean hasSuccessPath(char[][] matrix, int row, int line, char[] str, int index, boolean[][] isVisited) { // This indicates that all the elements in front of index have been matched successfully if (str.length == index) { return true; } // Unqualified return no passage if (row >= matrix.length || line == matrix[0].length || row < 0 || line < 0 || isVisited[row][line] || matrix[row][line] != str[index]) { return false; } ++index; // Mark that the road has passed isVisited[row][line] = true; // Try in all four directions boolean hasPath = hasSuccessPath(matrix, row + 1, line, str, index, isVisited) || hasSuccessPath(matrix, row, line + 1, str, index, isVisited) || hasSuccessPath(matrix, row - 1, line, str, index, isVisited) || hasSuccessPath(matrix, row, line - 1, str, index, isVisited); // If the isVisited value is not successfully restored to avoid affecting subsequent judgment, if it is true, it will cycle at the end of this step and will not affect subsequent judgment if (!hasPath) { isVisited[row][line] = false; } return hasPath; } }
Range of motion of robot
Title: there is a grid with M rows and N columns on the ground. A robot starts to move from one grid of coordinates (0, 0). He can move one grid in the left, right, up and down directions at a time, but he cannot enter that the sum of the digits of row coordinates and column coordinates is greater than the K value, and he must reach the grid through other grids before reaching each grid. For example: k=18, the robot can enter (35, 37), 3 + 5 + 3 + 7 = 18 < = 18 can enter, (36, 38), 3 + 6 + 3 + 8 = 20 > 18, can not enter.
- This topic is essentially the same as the previous one, only changing the limiting conditions and starting scope
- The starting range of the problem has been given (0, 0). Only recursive backtracking up, down, left and right is required. In addition, the number of paths should be recorded during recursion.
public class Main { public static int process(int k, int rows, int lines) { if (k < 0 || rows <= 0 || lines <= 0) { return 0; } boolean[][] isVisited = new boolean[rows][lines]; return movingCount(k, rows, lines, 0, 0, isVisited); } /** * @param k Limiting condition k * @param rows Total number of matrix rows * @param lines Total columns of matrix * @param i Number of current element rows * @param j Number of current element columns * @param isVisited Have you visited * @return Number of options */ private static int movingCount(int k, int rows, int lines, int i, int j, boolean[][] isVisited) { int count = 0; // If the constraints are met, it is marked and used as an expansion for recursive search if (canVisited(k, rows, lines, i, j, isVisited)) { isVisited[rows][lines] = true; count += movingCount(k, rows, lines, i + 1, j, isVisited) + movingCount(k, rows, lines, i - 1, j, isVisited) + movingCount(k, rows, lines, i, j + 1, isVisited) + movingCount(k, rows, lines, i, j - 1, isVisited); } return count; } private static boolean canVisited(int k, int rows, int lines, int i, int j, boolean[][] isVisited) { if (i >= 0 && i < rows && j >= 0 && j < lines && !isVisited[i][j] && getDigitSum(i) + getDigitSum(j) <= k) { return true; } return false; } // Calculate the sum of num and each digit private static int getDigitSum(int num) { int sum = 0; while (num > 0) {// Cannot = 0 sum += num % 10; num /= 10; } return sum; } }
Dynamic programming and greedy algorithm
Cut the rope πΏπ₯
Title: here is a rope with length n. please cut the rope into m segments. Both M and N are integers and are greater than 1. The length of each segment of rope is recorded as K1, K2... Km. What is the maximum product of their multiplication?
- For any value N, the problem can be decomposed into two word problems I and N-i (I from 1 to N-1). The maximum of these subproblems is the result. For each word problem, it can also be divided into smaller subproblems. It is obviously a classic recursive case.
Recursive implementation
public class Main { public static int processMain(int N) { if (N < 2) { return 0; } else if (N == 2) { return 1; } else if (N == 3) { return 2; } return process(N); } public static int process(int N) { if (N == 1) { return 1; } else if (N == 2) { return 2; } else if (N == 3) { return 3; } int max = 0; for (int i = 1; i <= N / 2; i++) { max = Math.max(max, process(i) * process(N - i)); } return max; } }
- Because the process function will be called many times during the operation, they will be repeated during recursion, resulting in an exponential increase in time complexity. If we record the value of each calculation, the complexity will be square, which is the idea of dynamic planning.
dynamic programming
public class Main { public static int processMain(int N) { if (N < 2) { return 0; } else if (N == 2) { return 1; } else if (N == 3) { return 2; } int[] maxValues = new int[N + 1]; maxValues[0] = 0; maxValues[1] = 1; maxValues[2] = 2; maxValues[3] = 3; int max = 0; //Subject thought of dynamic programming for (int i = 4; i <= N; i++) { max = 0; //Consider N as i and calculate the maximum value of i for (int j = 1; j <= i / 2; j++) { max = Math.max(max, maxValues[j] * maxValues[i - j]); } maxValues[i] = max; } //Returns the value return maxValues[N]; } }
Greedy thought
- When the remainder is not 1, we select as many 3 as possible. When the remainder is 1, we divide the previously separated 3 and remainder 1 into 2 and 2, so that we can spell the maximum value to the greatest extent.
- This greedy idea is not easy to understand and come up with. It needs strict mathematical proof. It will not be repeated here. The greedy algorithm can only be relatively familiar with as many question types as possible.
public class Main { public static int processMain(int N) { if (N < 2) { return 0; } else if (N == 2) { return 1; } else if (N == 3) { return 2; } // Use 3 as many as possible int timesOf3 = N / 3; // If the last 1 remains, the previously separated 3.1 should be converted to 2.2, so that it can be converted to the maximum. if (N % 3 == 1) { timesOf3 -= 1; } // 2 number of occurrences required int timesOf2 = (N - timesOf3 * 3) / 2; return (int) Math.pow(3, timesOf3) * (int) Math.pow(2, timesOf2); } }
Bit operation
Number of integer binary 1
Title: please implement a function, enter an integer and return the number of 1 contained in the binary system.
- There are negative numbers in the integer range, so we should fully consider the situation of negative numbers, especially when shifting the input negative value to the right, pay attention to the automatic complement of 1 in the highest bit.
Bit operation shift right input integer
public class Main { public static int processMain(int N) { int num=0; // Specifies 32 operations to process negative numbers for (int i = 0; i < 32; i++) { if ((N & 1) == 1) { ++num; } N>>=1; } return num; } }
Bit operation shift left 1
public class Main { public static int processMain(int N) { int num = 0; int flag = 1; while (flag != 0) { if ((flag & N) == flag) { ++num; } flag <<= 1; } return num; } }
Subtract the input integer by 1 to split the 1 of the integer
public class Main { public static int processMain(int N) { int num = 0; while (N != 0) { ++num; N = (N - 1) & N;// Each run will eliminate a 1, } return num; } }
High quality code
Normative
- Standardization of code: writing of ideas, standardization of code layout, naming of variable methods,
Integrity
- Code integrity: functional integrity, boundary test, negative test, and corresponding prompt for error handling.
Print from 1 to maximum N digits πΏπ₯
Title: given an integer N, print the maximum value of an integer from 1 to N.
- Obviously, this problem is not just a simple integer problem. If we use int or long int to represent the data size, it is very limited, so we need to use string to simulate the data operation
public class Main { public static void main(String[] args) { processMain(3); } public static void processMain(int N) { if (N <= 0) { return; } char[] arr = new char[N]; Arrays.fill(arr, '0'); // Continue without reaching the maximum while (!TreeIncrement(arr)) { PrintNumbers(arr); } } private static void PrintNumbers(char[] arr) { boolean isBegin = true; int len = arr.length; for (int i = 0; i < len; i++) { // The output should start from the first position that is not 0. Mark the start position with isBegin if (isBegin && arr[i] != '0') { isBegin = false; } if (!isBegin) { System.out.print(arr[i]); } } System.out.println(); } private static boolean TreeIncrement(char[] arr) { int nTakeOver = 0;// Carry flag 1 int len = arr.length; for (int i = len - 1; i >= 0; --i) { char nSum = (char) (arr[i] + nTakeOver); // Minimum + 1 if (i == len - 1) { nSum++; } if (nSum - '0' == 10) { // The highest bit becomes 10. At this time, it indicates overflow and returns if (i == 0) { return true; } else { // This bit becomes 10. To carry, proceed to the next operation. nTakeOver = 1;// Carry 1 arr[i] = '0';// The value becomes 0 } } else { // This value is not 10, so it is allowed to return directly arr[i] = nSum; return false; } } return false; } }
Delete linked list node
Title: to delete a node in O(1) time, you have to wait for the · head pointer of the single linked list to define a function to delete a node within the specified time complexity. The deleted node is set in the linked list.
- The single linked list cannot directly change the next pointer of the precursor node
- By copying the next value to the deletion node, you can delete the next node of the borrowing point to complete the deletion operation.
public class Main { public static void processMain(Node header, Node deleteNode) { if (header == null || deleteNode == null) { return; } //The deleted node is not the tail node. You can directly delete the next node by copying the content if (deleteNode.nextNode != null) { deleteNode.num = deleteNode.nextNode.num; deleteNode.nextNode = deleteNode.nextNode.nextNode; } else if (header == deleteNode) {//The header has only one node and is the deleted node header = null; } else {//The deleted borrowing point is the tail node, which can only be searched by traversal Node tail = header; while (tail.nextNode != deleteNode) { tail = tail.nextNode; } tail.nextNode = null; } } public static class Node { int num; Node nextNode; } }
Delete duplicate nodes of ordered linked list
Topic: how to delete duplicate nodes in a sorted linked list.
- A little simple, no thinking
public class Main { public static void processMain(Node header) { if (header==null) { return; } Node tail=header; Node preNode=header; while (preNode.nextNode!=null) { Node thisNode=preNode.nextNode; if (preNode.num!=thisNode.num) { preNode=thisNode; }else { preNode.nextNode=thisNode.nextNode; } } } public static class Node { int num; Node nextNode; } }
Adjust the array order so that odd numbers come first and even numbers come last
Title: enter an integer array and implement a function to adjust the number order in the array so that all odd numbers are in the first half of the number and all even bits are in the second half of the array.
- It's obviously part of a simple quick sort.
- However, we'd better write the comparison method alone, which can increase the reusability of the code and be more standardized.
public class Main { public static void processMain(int [] arr) { if (arr==null||arr.length==0) { return; } int left=0; int right=arr.length-1; while (left<right) { while (left<right&&!cmp(arr,left)) { left++; } while (left<right&&cmp(arr, right)) { right--; } if (left<right) { int temp=arr[left]; arr[left]=arr[right]; arr[right]=temp; } } } private static boolean cmp(int[] arr, int left) { return (arr[left]&1)==0; } }
Robustness
- Code robustness: judge whether the input meets the specification requirements, and deal with the input that does not meet the requirements appropriately.
Returns the penultimate node of the lin k ed list
Title: given a single linked list and integer K, return the penultimate node in the single linked list.
- The general idea is to traverse the linked list twice, calculate the total number of nodes for the first time, and then traverse the total number of nodes - K
- But we use two pointers with a difference of K between two steps to traverse together. At the end of the fast, the slow is what we are looking for
public class Main { public static Node processMain(Node header, int k) { if (header == null) { return null; } Node tailNode = header; int i = 0; // Ensure that the node exists while (tailNode != null && i < k) { tailNode = tailNode.nextNode; } // Because the node does not exist, there are not so many nodes in the linked list if (tailNode == null) { return null; } // The two pointers advance at the same time until the previous pointer reaches the end of the linked list Node preNode = header; while (tailNode != null) { tailNode = tailNode.nextNode; preNode = preNode.nextNode; } return preNode; } static class Node { int value; Node nextNode; } }
The entry node of a link in a linked list
Title: given the head node of a linked list, the linked list contains a ring and returns the ring in node.
- Start with node node1 where the fast and slow pointers meet for the first time. At this time, let another pointer point to the head node node2, and take the same number of steps is the ring entry node.
public class Main { public static Node processMain(Node header) { if (header == null) { return null; } boolean flag = false; Node quick = header; Node slow = header; while (quick.nextNode != null && quick.nextNode.nextNode != null) { slow = slow.nextNode; quick = quick.nextNode.nextNode; // If there is a loop, exit the loop if (slow == quick) { flag = true; break; } } // null if acyclic if (!flag) { return null; } // Take the same steps to find the ring node Node tailNode = header; while (tailNode != slow) { tailNode = tailNode.nextNode; slow = slow.nextNode; } // The intersection node is the in loop node return tailNode; } static class Node { int value; Node nextNode; } }
Reverse single linked list
Title: given the head node of a linked list, return the head node after the reversal of the linked list.
public class Main { public static Node processMain(Node header) { if (header == null) { return null; } Node preNode = null; Node tempNode = null; Node thisNode = header; while (thisNode != null) { tempNode = thisNode.nextNode; thisNode.nextNode = preNode; preNode = thisNode; thisNode = tempNode; } return preNode; } static class Node { int value; Node nextNode; } }
Merge two sort linked lists
Enter the header node of two ordered chain lists to return the merged ordered chain header node. The space complexity is required to be O(1).
Recursive implementation
public class Main { public static Node merge(Node header_1, Node header_2) { if (header_1 == null) { return header_2; } else if (header_2 == null) { return header_1; } Node mergeHeader = null; if (header_1.num < header_2.num) { mergeHeader = header_1; mergeHeader.nextNode = merge(header_1.nextNode, header_2); } else { mergeHeader = header_2; mergeHeader.nextNode = merge(header_1, header_2.nextNode); } return mergeHeader; } public static class Node { int num; Node nextNode; } }
Loop implementation
public class Main { public static Node merge(Node header_1, Node header_2) { if (header_1 == null) { return header_2; } else if (header_2 == null) { return header_1; } Node mergeHeader = header_1.num < header_2.num ? header_1 : header_2; Node tailNode = mergeHeader; Node tempNode; while (header_1 != null && header_2 != null) { if (header_1.num < header_2.num) { tailNode.nextNode = header_1; header_1 = header_1.nextNode; } else { tailNode.nextNode = header_2; header_2 = header_2.nextNode; } tailNode = tailNode.nextNode; } tailNode.nextNode = header_1 == null ? header_2 : header_1; return mergeHeader; } public static class Node { int num; Node nextNode; } }
Substructure of tree π
Enter the root nodes a and B of two binary trees to judge whether B is a subtree of A.
- Traverse the A tree. If A node has the same value as the B root node, judge from the node.
- The judgment of each state needs to be considered. When a judgment is successful, the whole program returns true.
- Note: when calling, you must pay attention to whether it is null to avoid exceptions.
public class Main { public static boolean subTree(Node A, Node B) { boolean isSub = false; if (A != null && B != null) { if (A.num == B.num) { isSub = com(A, B);// Judge whether B is A subtree starting from A } // draw a blank if (!isSub) { isSub = subTree(A.leftNode, B);// Try to the left } // draw a blank if (!isSub) { isSub = subTree(A.rightNode, B);// Try to the right } } return isSub; } private static boolean com(Node a, Node b) { if (b == null) { return true; } if (a == null) { return false; } if (a.num != b.num) { return false; } // Ensure that each sub step is established return com(a.leftNode, b.leftNode) && com(a.rightNode, b.rightNode); } public static class Node { int num; Node leftNode; Node rightNode; } }
Solving interview ideas
Visualization of abstract problems
Image of binary tree
Title: Please complete a function, enter the root node of a tree, and return the root node of the binary tree image.
- The large problem can be replaced by a sub problem, which can be transformed into a recursive problem
- The problem is to take each node as the root node, exchange the left and right nodes, and pay attention to prevent node coverage.
Method 1
public class Main { public static Node resverse(Node header) { if (header==null) { return null; } //Why save it first? Because the direction of header.leftNode will be changed in the next step, so the previous Node cannot be located Node tempNode=header.leftNode; header.leftNode=resverse(header.rightNode); header.rightNode=resverse(tempNode); return header; } public static class Node { int num; Node leftNode; Node rightNode; } }
Method 2
public class Main { public static void resverse(Node header) { if (header==null) { return; } if (header.leftNode==null&&header.rightNode==null) { return; } Node tempNode=header.leftNode; header.leftNode=header.rightNode; header.rightNode=tempNode; if (header.leftNode!=null) { resverse(header.leftNode); } if (header.rightNode!=null) { resverse(header.rightNode); } } public static class Node { int num; Node leftNode; Node rightNode; } }
Judging symmetric binary tree π
Title: Please complete a function to judge whether the binary tree is symmetrical. Given the root node, output boolean data. (all non full binary trees are asymmetric)
- In the case of ensuring that the node is not empty, ensure that each time you enter a function, the two nodes you come to are symmetrical relative to the tree
- Create two pointers tail1 and tail2, both starting from the header root node. When the two pointers move, always ensure that the pace direction is always opposite, that is, tail1 left, tail2 right, tail1 right and tail2 left.
public class Main { public static boolean isSym(Node header) { return isSym(header,header); } private static boolean isSym(Node tail1, Node tail2) { if (tail1==null&&tail2==null) { return true; } if(tail1==null||tail2==null) { return false; } if(tail1.num!=tail2.num) { return false; } // Keep the pace opposite in both directions return isSym(tail1.leftNode,tail2.rightNode)&& isSym(tail1.rightNode,tail2.leftNode); } public static class Node { int num; Node leftNode; Node rightNode; } }
Print matrix clockwise
Title: print matrix clockwise.
- The macro scheduling method is adopted, which is not limited to each detail problem.
- Just get and limit the output according to the limit conditions of each large cycle.
class Main { public static void print(int[][] arr) { int firstRow = 0; int firstLine = 0; int secondRow = arr.length - 1; int secondLine = arr[0].length - 1; // Macro restrictions on word printing while (firstLine <= secondLine && firstRow <= secondRow) { print(arr, firstRow++, firstLine++, secondRow--, secondLine--); } } // Macro word output function private static void print(int[][] arr, int firstRow, int firstLine, int secondRow, int secondLine) { if (firstLine == secondLine) { for (int i = firstRow; i <= secondRow; i++) { System.out.print(arr[i][firstLine] + " "); } } else if (firstRow == secondRow) { for (int i = firstLine; i <= secondLine; i++) { System.out.print(arr[firstRow][i] + " "); } } else { int curR = firstRow; int curL = firstLine; while (curL != secondLine) { System.out.print(arr[firstRow][curL++] + " "); } while (curR != secondRow) { System.out.print(arr[curR++][secondLine] + " "); } while (curL != firstLine) { System.out.print(arr[secondRow][curL--] + " "); } while (curR != firstRow) { System.out.print(arr[curR--][firstLine] + " "); } } } }
Concretization of abstract problems
Data stack structure containing min
Title: Please complete a stack structure to obtain the smallest element in the stack at any time. The time complexity of push, pop and getMin functions is O(1)
- The time complexity of push and pop functions is O (1)
- To achieve O(1), the getMin function specifies that the minimum value can be obtained directly. In fact, at the beginning of the push, we can only judge the size relationship between the element pushed in and the minimum element in the previous stack, and the previous minimum value is also judged according to the element pushed in each time. Well, I can only judge and record when pushing.
class MyStack { private Stack<Integer> stack; private Stack<Integer> minStack; public MyStack() { this.stack = new Stack<>(); this.minStack = new Stack<>(); } public void push(Integer x) { stack.add(x); minStack.add(!minStack.isEmpty() && minStack.peek() < x ? minStack.peek() : x); } public Integer pop() { if (stack.isEmpty()) return null; minStack.pop(); return stack.pop(); } public int getMin() { return minStack.peek(); } }
Push pop sequence of stack π
Title: given two integer sequences, the first is the push in sequence. Elements can pop up at any time during the push in process. Judge whether the second sequence may be the correct pop-up sequence.
public class Main { public static boolean isSuccess(int[] pushARR, int[] popArr) { if (pushARR == null || popArr == null || pushARR.length != popArr.length) { return false; } int N = pushARR.length; Stack<Integer> stack = new Stack<>(); int pushIndex = 0, popIndex = 0; while (popIndex < N) { while (stack.isEmpty() || stack.peek() != popArr[popIndex]) { // If the input has not been matched with popArr, exit and return false // The stack must not be empty at this time. If it is empty: // The last matching is successful, and an element is deleted from the stack. At this time, the pushindex and popindex values are the same // -If both are N, it is not satisfied after the last deletion: popIndex == N, and the cycle will not be entered // -If popindex < n, the judgment cannot be entered if (pushIndex == N) { return false; } // If the stack space is limited, you can judge whether the stack is full here // if(stack.size() > maxNum) // return false; stack.push(pushARR[pushIndex++]); } stack.pop(); popIndex++; } // If you exit the loop, popIndex==N and stack.isEmpty() are true return true; } }
Print binary tree from top to bottom
Title: Please complete a function to print the binary tree horizontally given the head node.
public class Main { public static void process(Node root) { if (root==null) { return; } Deque<Node> deque=new ArrayDeque<>(); while (!deque.isEmpty()) { Node tempNode=deque.poll(); System.out.print(tempNode.num+" "); // Left not empty if (tempNode.leftNode==null) { deque.add(tempNode.leftNode); } // Right not empty if (tempNode.rightNode==null) { deque.add(tempNode.rightNode); } } } public static class Node{ int num; Node leftNode; Node rightNode; } }
Branch prints a binary tree from top to bottom π
Title: print the node value of each line on different lines based on the previous question.
public class Main { public static void process(Node root) { if (root == null) { return; } // Traverse the last node of the layer Node thisFloorLastNode = root; // Traverse the last node of the next layer of this layer node, which is used to update thislooplastnode as the end flag Node nextFloorLastNode = null; Deque<Node> deque = new ArrayDeque<>(); while (!deque.isEmpty()) { Node tempNode = deque.poll(); System.out.print(tempNode.num + " "); // Whenever there are nodes on the left or right, the nextFloorLastNode node must be updated if (tempNode.leftNode == null) { deque.add(tempNode.leftNode); nextFloorLastNode = tempNode.leftNode; } if (tempNode.rightNode == null) { deque.add(tempNode.rightNode); nextFloorLastNode = tempNode.rightNode; } // When the layer ends, the layer end flag should be updated. if (tempNode == thisFloorLastNode) { thisFloorLastNode = nextFloorLastNode; nextFloorLastNode = null; System.out.println();// branch } } } public static class Node { int num; Node leftNode; Node rightNode; } }
Circling printing binary tree node
Title: implement a function. Given the root node, the first line of nodes is printed from left to right, the second line is printed from right to left, the third line is printed from left to right, and so on.
- Assuming that line n traverses from left to right, the nodes of layer N+1 will be obtained through layer n. the nodes of layer N+1 are from left to right, but our requirements of layer N+1 are from right to left. Obviously, the stack is used to store the elements traversed through layer n.
- Moreover, when storing layer N+1, the elements of layer N cannot be overwritten, so two stack structures are used.
public class Main { public static void process(Node root) { if (root == null) { return; } // Create two stack structures Stack<Node>[] stack = new Stack[] { new Stack<>(), new Stack<>() }; int current = 0;// Stack index of the layer to be traversed int next = 1;// When traversing (deleting) stack[current], fill the stack space at stack[next] stack[current].push(root);// Put the root node into the current stack while (!stack[0].isEmpty() || !stack[1].isEmpty()) { Node tempNode = stack[current].pop(); // Because the filling stack[next] order required to traverse different layers is different, it should be separated. if (current == 0) { // If it is not empty, it must be put on the stack if (tempNode.leftNode != null) { stack[next].push(tempNode.leftNode); } if (tempNode.rightNode != null) { stack[next].push(tempNode.rightNode); } } else { // If it is not empty, it must be put on the stack if (tempNode.rightNode != null) { stack[next].push(tempNode.rightNode); } if (tempNode.leftNode != null) { stack[next].push(tempNode.leftNode); } } // If the current stack is empty, it means that the node of this layer ends and traverses another stack structure if (stack[current].isEmpty()) { System.out.println(); // Index of conversion stack structure current = 1 - current; next = 1 - next; } } } public static class Node { int num; Node leftNode; Node rightNode; } }
Postorder traversal of binary search tree π
Title: given an array, judge whether the array can be converted into a convenient result of post order traversal of a search binary tree, and return boolean type data.
- You still need to know the post order traversal characteristics of binary search tree. The last element can divide the previous element into two parts, one small and one large.
- Then use recursion to continue to judge whether the elements divided into two parts are consistent
public class Main { public static boolean process(int[] arr) { if (arr == null || arr.length <= 2) { return true; } return process(arr, 0, arr.length - 1); } /** * @param arr target array * @param start Start index * @param end End index * @return Compliance with characteristics */ private static boolean process(int[] arr, int start, int end) { // Recursive end condition if (start == end) { return true; } int gap = arr[end]; int i = start; // The last node of post order traversal can divide the search binary tree into two parts, small on the left and large on the right for (; i < end; i++) { if (arr[i] > gap) { break; } } // At this time, arr[i] is greater than the minimum value of gap int j = i; // Judge whether arr[i...end-1] is larger than gap for (; j < end; ++j) { if (arr[j] < gap) { return false; } } // The left and right are judged respectively // Left is small, excluding arr[i] boolean left = process(arr, start, i - 1); // The right is large, including arr[i], but note that it cannot include arr[end], because it has been judged boolean right = process(arr, i, end - 1); // Only when both sides are established can it be established as a whole return left && right; } public static class Node { int num; Node leftNode; Node rightNode; } }
A binary tree and a path
Title: given a binary tree root node and an integer Intend, print all paths from the root node to the leaf node and all paths with Intend in the binary tree.
- Obviously, it is a simple recursive backtracking function.
public class Main { public static void process(Node root, int intend) { if (root == null) { return; } Stack<Node> stack = new Stack<>(); process(root, intend, 0, stack); } /** * @param node Current node * @param intend target value * @param curNum Current value * @param stack Storage path */ private static void process(Node node, int intend, int curNum, Stack<Node> stack) { curNum += node.num; stack.add(node);// Path addition // Is a leaf node and is the same as the target value if (node.leftNode == null && node.rightNode == null && intend == curNum) { for (Node n : stack) { System.out.print(n.num); } System.out.println(); } if (node.leftNode != null) { process(node.leftNode, intend, curNum, stack); } if (node.rightNode != null) { process(node.rightNode, intend, curNum, stack); } // Path deletion stack.pop(); } public static class Node { int num; Node leftNode; Node rightNode; } }
Decomposition problem simplification
Replication of complex linked list
Title: given a linked list, in addition to nextNode, the linked list also has a pointer complexNode, which can point to any position in the linked list. Given the head node, the range is the head node after copying the node.
Node type:
class Node {
int value;
Node nextNode;
Node complexNode;
}
Implementation with map
public class Main { public static Node copy(Node header) { if (header == null) { return null; } HashMap<Node, Node> map = new HashMap<Node, Node>(); Node tail = header; // Traversal fill hash table while (tail != null) { map.put(tail, new Node(tail.value)); tail = tail.nextNode; } tail = header; // According to the one-to-one correspondence of the hash table, connect and copy the linked list according to the given linked list. while (tail != null) { map.get(tail).nextNode = map.get(tail.nextNode); map.get(tail).complexNode = map.get(tail.complexNode); tail = tail.nextNode; } // Return the assigned chain header node and get it directly in the map return map.get(header); } class Node { int value; Node nextNode; Node complexNode; } }
map implementation of relative position simulation
public class Main { public static Node copy(Node header) { if (header == null) return null; Node tail = header; Node n; // Adding a copy node after each original linked list node essentially simulates the map method using location information while (tail != null) { n = tail.nextNode; tail.nextNode = new Node(tail.value); tail.nextNode.nextNode = n; tail = n; } tail = header; Node copyTail; // Copy and process the complexNode node (judge only the original node) while (tail != null) { copyTail = tail.nextNode; if (tail.complexNode != null) copyTail.complexNode = tail.complexNode.nextNode; tail = tail.nextNode.nextNode;//This node is not empty. There must be a next node } // Determine the head node of the original linked list and the copied linked list. Node copyHeader = header.nextNode;// If not marked in advance, it will be recycled tail = header; // Separation of original linked list and copy linked list while (tail != null) { n = tail.nextNode.nextNode; copyTail = tail.nextNode;// Copy node of linked list if (n != null) copyTail.nextNode = n.nextNode;// Linked list for connection replication tail.nextNode = n;// Connect the original linked list tail = n;// Update to judge the next round } return copyHeader; } class Node { int value; Node nextNode; Node complexNode; } }
Binary search tree and bidirectional linked list π
Title: enter a search binary tree and turn the binary tree into a two-way linked list. leftNode is the predecessor node and rightNode is the successor node.
- Pay attention to the traversal direction to prevent the occurrence of null pointers.
- Pay attention to the changes of precursor nodes in the process of traversing left and right.
public class Main { public static Node process(Node root) { Node firstNode = null; convertNode(root, firstNode); return firstNode; } /** * @param pThisNode Current node * @param pLastNode The previous node of the order traversal in a binary tree */ private static void convertNode(Node pThisNode, Node pLastNode) { if (pThisNode == null) { return; } // Find node left if (pThisNode.leftNode != null) { convertNode(pThisNode.leftNode, pLastNode); } // Connect precursor node pThisNode.leftNode = pLastNode; // Connect the successor node of the previous node if (pLastNode != null) { pLastNode.rightNode = pThisNode; } // If there is a right node, the node is traversed to the right as a precursor node if (pThisNode.rightNode != null) { convertNode(pThisNode.rightNode, pThisNode); } } class Node { int value; Node leftNode; Node rightNode; } }
Full arrangement of strings π
Title: given a string, output all permutations and combinations of all strings.
- We usually think of putting all characters in the front in turn, such as ABC. The first digit is A, B and C, and then judge the second digit. Then how can we mark that the character has been arranged in front of us in the string?
- If we use the subscript method, a subscript will be generated in each selection, which will be very messy.
- So we can use a subscript to guide how many elements we have set in front by putting the character we want to put in front directly in front (exchanging the character with the following one at a time).
- However, if the data sequence has been disrupted during subsequent calls after the exchange, we may cause duplication, so we will change the data exchange to the original position after each run.
- However, when there are repeated characters in the data, the repeated full arrangement will appear. This is to judge whether the exchanged characters are the same as those previously exchanged. If they are the same, there is no need to exchange them
class Main { public static List<String> list=new ArrayList<>(); public static void process(String string){ char[] chars = string.toCharArray(); process(chars,0); } private static void process(char[] chars, int i){ if (i==chars.length){//result list.add(new String(chars)); return; } boolean[] isVisited=new boolean[26];//Only uppercase letters are available by default for (int j=i;j<chars.length;j++){ if (!isVisited[chars[j]-'A']){//Repeat isVisited[chars[j]-'A']=true; swap(chars,i,j);//exchange process(chars,i+1);//recursion swap(chars,i,j);//recovery } } } private static void swap(char[] chars,int i, int j) { char c = chars[i]; chars[i]=chars[j]; chars[j]=c; } }
Optimize time and space efficiency
- Strings in JAVA are often spliced with StringBuffer, which will waste a lot of space and time.
Time efficiency
Minimum number of k
Title: given a random integer array and an integer k, output the smallest K numbers in the array.
Implementation of partition in fast scheduling subprocess
- Will change the array itself
- Time complexity: O(N)
public class Main { public static void process(int[] arr, int k) { if (arr == null || arr.length == 0 || arr.length < k) { return; } int left = 0; int right = arr.length - 1; // Take partition as the dividing point int index = partition(arr, 0, right); while (index != k - 1) { // The partition benchmark value is too large if (index > k - 1) { right = index - 1; index = partition(arr, left, right); } else {// The partition benchmark value is too small left = index + 1; index = partition(arr, left, right); } } // output data for (int i = 0; i < k; i++) { System.out.println(arr[i]); } } // Fast scheduling subprocess private static int partition(int[] arr, int left, int right) { int base = arr[left]; int i = left; int j = right; while (i != j) { while (i != j && arr[j] >= base) { j--; } while (i != j && arr[i] <= base) { i++; } int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } int temp = arr[left]; arr[left] = arr[i]; arr[i] = temp; return i; } }
Priority queue implementation
- Time complexity O(N*logk)
public class Main { public static void process(int[] arr, int k) { if (arr == null || arr.length == 0 || arr.length < k) { return; } // Big root pile PriorityQueue<Integer> deQueue = new PriorityQueue<>((o1, o2) -> Integer.compare(o2, o1)); // Keep the large root heap data equal to k for (int i : arr) { if (deQueue.size() < k) { deQueue.add(i); } else if (deQueue.peek() > i) { deQueue.poll(); deQueue.add(i); } } // output data while (!deQueue.isEmpty()) { System.out.println(deQueue.poll()); } } }
Median in data stream
Title: given consecutive unknown integers, the median in these trees can be obtained at any time with time complexity O(1).
- Build two heaps, a large pile and a small pile. Large piles of smaller elements and small piles of larger elements
- Keep the difference in the number of elements between the two heaps no more than 1
- If it exceeds 1, put the top element of the large pile into the small pile or put the top element of the small pile into the large pile.
- In this way, the median is always only related to the top elements of the two heaps
class Tree { private PriorityQueue<Double> little;// Save larger data and take out smaller elements private PriorityQueue<Double> large;// Save smaller data and take out larger elements public Tree() { little = new PriorityQueue<>(); large = new PriorityQueue<>((o1, o2) -> Double.compare(o2, o1)); } public void add(Double x) { // If there are no elements, the element is stored in any one of them first. if (little.size() == 0 && large.size() == 0) large.add(x); else { // Put it in that pile if (large.peek() < x) { little.add(x); } else { large.add(x); } // If the absolute value of the difference between the two heap sizes is greater than 1, adjust it to better output the median. if (little.size() - large.size() > 1) large.add(little.poll()); else if (little.size() - large.size() < -1) little.add(large.poll()); } } public Double getMed() { if (little.size()==0&&large.size()==0) return null; else if (little.size() == large.size()) return (little.peek() + large.peek()) / 2; else if (little.size() - large.size() > 0) return little.peek(); else return large.peek(); } }
Maximum sum of consecutive subarrays π
Title: given an array, all of which are integers, return the maximum value of the cumulative sum composed of one or more consecutive elements in the array. Time complexity O(N)
- If you want to obtain the continuous cumulative sum and maximum value, the previous cumulative sum is set as a non negative number. If the previous cumulative sum value is negative, it cannot be included in the current calculated maximum cumulative sum.
public class Main { public static Integer maxLenNum(int[] arr) { if (arr == null) { return null; } // Record maximum int res = Integer.MIN_VALUE; int cur = 0; for (int i : arr) { // cur records are accumulated and will not be updated now. They will be updated after judgment. cur += i; res = Math.max(res, cur); // Cur records the cumulative sum. If the cumulative sum is less than 0, reset cur to 0 and start accumulating again. cur = Math.max(cur, 0); } return res; } }
Number of occurrences of 1 in integers 1~N π
Title: the number of occurrences of 1 from all integers 1~N.
Solution:
- For example: 1 ~ 46372 and 1 ~ 12345
- First split 76372 into 1 ~ 6372 and 6373 ~ 76372
- First look at 6373 ~ 76372, the highest level 7. When the highest level is not zero, calculate the number of occurrences of 1 in the highest level and the number of occurrences of 1 other than the highest level when the highest level occurs.
- If the highest bit is not 1, it is the highest bit for 6373 ~ 76372. There are 10 ^ 4, which can only appear in 10000 ~ 19999.
- The highest bit is 1, like 2346 ~ 12345. 10000 bits is 10000 ~ 12345, which is 2345 + 1
- Calculate the number of occurrences of 1 in the highest order (10000 bits) in 6373 ~ 76372, which is a simple permutation and combination. 7 *(10^4) * 4
- At this time, the calculation of 1 in 6373 ~ 76372 is completed, and then the occurrence times of 1 in 1 ~ 6372 are calculated and implemented recursively.
public class Main { public static int process(String s) { if (s == null || s.length() == 0) { return 0; } char[] arr = s.toCharArray(); return process(arr, 0); } private static int process(char[] arr, int index) { // Judge whether the highest bit is 1, and then judge the number of times 1 appears in the highest bit int first = arr[index] - '0'; // See how many bits are left in addition to the highest bit, which is used to judge the occurrence times of the highest bit and the remaining number after removing the highest bit int len = arr.length - index; // If this is the last bit, judge the number of 1 in the value ≤ first according to first. if (len == 1 && first == 0) { return 0; } if (len == 1 && first > 0) { return 1; } // Record the number of occurrences of the current highest 1 int numFirstDigit = 0; // If the highest bit is not > 1, the number of occurrences of 1 in the highest bit is determined as a multiple of 10 // For example, 42346 numFirstDigit is the number of the highest 1 in 10000 ~ 42346, that is, 10000 ~ 19999 // If it is 1, the number of occurrences of the highest bit is determined according to the value after the current highest bit // For example, 12345 is 10000 ~ 12345 if (first > 1) { numFirstDigit = (int) Math.pow(10, len - 1); } else if (first == 1) { // Start with 0 and don't forget + 1 numFirstDigit = Integer.parseInt(new String(arr).substring(index + 1)) + 1; } // When the highest bit is < = first, the number of 1 after the current highest bit. // Like 42346 // The highest 4, numOtherDigits, is 2347 ~ 42346. Excluding the number of 1 in 10000 bits, the number of 10000 bits has been obtained, which is numFirstDigit int numOtherDigits = first * (len - 1) * ((int) Math.pow(10, len - 2)); // According to the previous example: numsecure is the number of 1 in the remaining 1 ~ 2346. Pass it back. int numRecursive = process(arr, index + 1); return numFirstDigit + numOtherDigits + numRecursive; } }
A digit in a sequence of numbers π
Title: numbers are serialized into a character sequence in the format of 012345678910111213145. In this sequence, bit 5 (counting from 0) is 5, bit 13 is 1, bit 19 is 4, and so on. Please write a function to find any number corresponding to the nth bit.
- First determine that the value is in a sequence of several bits.
- The index position in the sequence is determined according to the value and several bits.
public class Main { public static int process(int index) { if (index < 0) { return -1; } int digits = 1; while (true) { // How many digits are there in total int numbers = countOfIntegers(digits); // numbers * digits means that the digits occupy so many position indexes if (index < numbers * digits) {// Is it within the range of digits return digitAtIndex(index, digits); } // Position occupation after iteration index -= digits * numbers; ++digits; } } private static int digitAtIndex(int index, int digits) { // (int)Math.pow(10, digits-1) indicates the total number of digits less than digits + 1 // index / digits indicates the number of digits at this time - 1 // The value we want is in number int number = (int) Math.pow(10, digits - 1) + index / digits; // Index% digits indicates the number digit // Indexfromlight is the number from the right, and the number of digits is our target value int indexFromRight = digits - index % digits; // Put the target value in one bit number /= ((int) Math.pow(10, indexFromRight - 1)); // Returns a value of bits return number % 10; } private static int countOfIntegers(int digits) { if (digits == 1) { return 10; } int count = (int) Math.pow(10, digits - 1); return 9 * count; } }
Arrange the array into the smallest number
Title: given an array, splice all the numbers in the array into a number, and print the smallest of all the spliced numbers. For example, {3, 32, 321}, the minimum value of composition is 321323.
- Two numbers N and m, if nm < Mn, print n first and then m, otherwise, print m first and then n.
- The idea can be extended to N numbers and the extension process is simple.
- In addition, if the array is too long and cannot be stored as an integer, you need to use StringBuffer to store judgment.
public class Main { public static void process(int[] arr) { if (arr == null || arr.length == 0) { return; } String[] strings = new String[arr.length]; // Prevent overflow for (int i = 0; i < arr.length; i++) { strings[i] = String.valueOf(arr[i]); } // Sort as needed Arrays.sort(strings, (o1, o2) -> (o1 + o2).compareTo(o2 + o1)); for (String string : strings) { System.out.print(string); } } }
Translate numbers into characters
Title: given a numeric string, translate it into letters according to the following rules. There are several ways to ask. Rules: 1 – > 'a', 2 – > 'b', 3 – > 'c',..., 26 – > 'z'.
- It's just a recursive attempt model from left to right.
public class Main { public static int process(String s, int i) { // If there is no one left or there is one left, it will return 1 (ensure that the remaining is not 0). If it is not returned at this time, it means that at least two characters are left if (s.length() == i || (s.length() == i + 1 && s.charAt(i) != '0')) return 1; if (s.charAt(i) == '0') return 0;// There are no matching elements starting with 0 int res = process(s, i + 1);// One character if (Integer.parseInt(s.substring(i, i + 2)) <= 26) res += process(s, i + 2);// If the matching conditions are met, two character matching is performed return res;// The accumulated result is returned } }
Maximum value of gift
Title: given an integer matrix, starting from the top left to the bottom right, it can only move down or right. What is the largest on the path?
- Simple recursive attempt model.
- It can be transformed into dynamic programming.
Violent recursion
public class Main { public static int process(int[][] arr) { if (arr == null || arr.length == 0 || arr[0].length == 0) { return 0; } return process(arr, arr.length - 1, arr[0].length - 1, 0, 0); } public static int process(int[][] arr, int M, int N, int row, int line) { if (row == M && line == N) { return arr[row][line]; } if (row == M) { return process(arr, M, N, row, line + 1) + arr[row][line]; } if (line == N) { return process(arr, M, N, row + 1, line) + arr[row][line]; } return Math.max(process(arr, M, N, row + 1, line), process(arr, M, N, row, line + 1)) + arr[row][line]; } }
dynamic programming
public class Main { public static int process(int[][] arr) { if (arr == null || arr.length == 0 || arr[0].length == 0) { return 0; } int M = arr.length; int N = arr[0].length; int[][] dp = new int[M][N]; // Critical value filling dp[M - 1][N - 1] = arr[M - 1][N - 1]; for (int i = M - 2; i >= 0; --i) { dp[i][N - 1] = dp[i + 1][N - 1] + arr[i][N - 1]; } for (int i = N - 2; i >= 0; --i) { dp[M - 1][i] = dp[M - 1][i + 1] + arr[M - 1][i]; } // dp table is populated according to dynamic programming dependency for (int row = M - 2; row >= 0; --row) { for (int line = N - 2; line >= 0; --line) { dp[row][line] = Math.max(dp[row + 1][line], dp[row][line + 1]) + arr[row][line]; } } return dp[0][0]; } }
The longest substring without duplicate characters π
Title: given a string containing only lowercase letters, find the longest non repeating substring, and return the length of the longest substring. For example, "asadfgsg" is up to "sadfg" and returns 5
- Suppose there is a traversal subscript i, and the beginning of the longest non repeating substring ending with arr [i] is: the position of the last occurrence of the ARR [i] character and the position closest to I in order to meet other behaviors in front of I are the longest substring.
public class Test { public static int maxUnique(String str) { if (str == null || str.equals("")) { return 0; } char[] chas = str.toCharArray(); //Record the last occurrence of each character int[] map = new int[26]; for (int i = 0; i < 26; i++) { map[i] = -1;// The initial value of the last occurrence is - 1 } int len = 0;// Result value int pre = -1;// Record of the longest forward extension subscript position of the current string int cur = 0;// Temporary pre for updating pre for (int i = 0; i != chas.length; i++) { // The longest position of the preceding string is the last occurrence position of arr[i] and the longest position satisfying other characters pre = Math.max(pre, map[chas[i]-'a']); cur = i - pre;// At this time, the longest stretch position of the string, cur, is the length of the longest non repeated string ending with arr[i]. len = Math.max(len, cur);//len update for max map[chas[i]] = i;//Record the position of the character at this time, which is used to find the duplicate position next time } // Return results return len; } }
Balance of time efficiency and space efficiency
- Generally speaking, we pursue time efficiency better than space, but for embedded, sometimes we waste some time to reduce space consumption, because the embedded memory is very limited.
Ugly number
Title: we call the number containing only prime factors 2, 3 and 5 ugly number, and find the nth ugly number in the positive integer field. In addition, it is customary to take 1 as the first ugly number.
- General idea: the first integer 1~N determines whether it is an ugly number in turn.
- Optimization idea: different from seeking prime numbers, ugly numbers are predictable and can be obtained directly by multiplying ugly numbers by 2, 3 or 5. So we just need to find the ugly number on the basis of 1.
- It should be noted that after the nth-1st ugly number is obtained, the nth ugly number cannot be derived directly from N-1, because the nth ugly number may be obtained from the number in front of N-1.
public class Main { public static int process(int N) { if (N<=0) { return 0; } int[] uglyNumber=new int[N]; uglyNumber[0]=1; int index=1; // multiply_2_3_5 the three indexes point to values close to the size of uglyNumber[index] int multiply_2=0; int multiply_3=0; int multiply_5=0; int nextUglyNum; while (index<N) { nextUglyNum=Math.min(uglyNumber[multiply_2]*2 ,Math.min(uglyNumber[multiply_3]*3, uglyNumber[multiply_5]*5)); uglyNumber[index]=nextUglyNum; while (multiply_2!=index&&uglyNumber[multiply_2]*2<=nextUglyNum) { ++multiply_2; } while (multiply_3!=index&&uglyNumber[multiply_3]*3<=nextUglyNum) { ++multiply_3; } while (multiply_5!=index&&uglyNumber[multiply_5]*5<=nextUglyNum) { ++multiply_5; } ++index; } return uglyNumber[N-1]; } }
Reverse order pairs in an array π
Title: two numbers in the array. If the first number is greater than the last number, then the two numbers form an inverse pair. Enter an array and return the number of pairs in reverse order.
- Merge sort process is similar to decimal sum.
- Why can one-way size judgment be realized in the process of merging and sorting? In fact, merge sorting maintains the local order of data in the sorting process. When merging, there is a relative positional relationship between the two sub arrays as a whole. This is why the size judgment in one direction can only be performed when merging.
public class Main { public static int process(int[] arr) { if (arr == null || arr.length == 0) { return 0; } return divideTest(arr, 0, arr.length - 1, new int[arr.length]); } private static int divideTest(int[] arr, int left, int right, int[] temp) { if (left < right) { int m = (left + right) / 2; return divideTest(arr, left, m, temp) // Sum of left + divideTest(arr, m + 1, right, temp)// Sum of right + mergeTest(arr, left, m, right, temp);// The sum is formed during the combination of left and right } return 0; } private static int mergeTest(int[] arr, int left, int m, int right, int[] temp) { int i = left; int j = m + 1; int tempIndex = 0; int res = 0; while (i <= m && j <= right) { // The difference between and decimal sum is greater than or less than the sign sum, and there is no multiplication by arr[i] res += arr[i] > arr[j] ? (right - j + 1) : 0; temp[tempIndex++] = arr[i] < arr[j] ? arr[i++] : arr[j++]; } while (i <= m) temp[tempIndex++] = arr[i++]; while (j <= right) temp[tempIndex++] = arr[j++]; System.arraycopy(temp, 0, arr, left, tempIndex); return res; } }
Decimal sum based on merge sort π
Additional topics: small sum problems and reverse order pairs of small sum problems in an array. The numbers on the left of each number that are smaller than the current number are accumulated, which is called the small sum of this array. Find the small sum of an array. Example: [1,3,4,2,5] a number smaller than 1 on the left of 1, no; 3 the number smaller than 3 on the left, 1; 4 numbers smaller than 4 on the left, 1 and 3; 2 the number smaller than 2 on the left, 1; 5 numbers smaller than 5 on the left, 1, 3, 4, 2; Therefore, the small sum is 1 + 1 + 3 + 1 + 1 + 3 + 4 + 2 = 16
public class Main { public static int process(int[] arr) { if (arr == null || arr.length == 0) { return 0; } return divideTest(arr, 0, arr.length - 1, new int[arr.length]); } private static int divideTest(int[] arr, int left, int right, int[] temp) { if (left < right) { int m = (left + right) / 2; return divideTest(arr, left, m, temp) // Sum of left decimal and + divideTest(arr, m + 1, right, temp)// Sum of right decimal and + mergeTest(arr, left, m, right, temp);// The sum of decimals formed during the combination of the left and right } return 0; } private static int mergeTest(int[] arr, int left, int m, int right, int[] temp) { int i = left; int j = m + 1; int tempIndex = 0; int res = 0; while (i <= m && j <= right) { // If left is small, decimal place will appear. The number is determined by the right side. res += arr[i] < arr[j] ? arr[i] * (right - j + 1) : 0; temp[tempIndex++] = arr[i] < arr[j] ? arr[i++] : arr[j++]; } while (i <= m) temp[tempIndex++] = arr[i++]; while (j <= right) temp[tempIndex++] = arr[j++]; System.arraycopy(temp, 0, arr, left, tempIndex); return res; } }
The first common node of two linked lists
Title: given two acyclic single linked lists with common parts, implement a function to return the first common node.
- All the nodes are traversed to calculate the node difference step, and then restart the long linked list. After the difference step, the short and long nodes are traversed at the same time. The first same node is the first encounter node.
public class Main { public static Node noLoop(Node head1, Node head2) { if (head1 == null || head2 == null) { return null; } Node cur1 = head1; Node cur2 = head2; int n = 0; //Calculate linked list difference while (cur1.next != null) { n++; cur1 = cur1.next; } while (cur2.next != null) { n--; cur2 = cur2.next; } if (cur1 != cur2) { return null; } cur1 = n > 0 ? head1 : head2; cur2 = cur1 == head1 ? head2 : head1; n = Math.abs(n); //The long one goes to the same position as the short one while (n != 0) { n--; cur1 = cur1.next; } //Judge whether there is the same node. If there is no node, it will go to the end and return null while (cur1 != cur2) { cur1 = cur1.next; cur2 = cur2.next; } return cur1; } public static class Node{ int value; Node next; } }
Abilities in interview
- communication skills
- learning ability
Knowledge transfer ability
Find a number in a sorted array π
Title: given an ordered array and a value, return the number of times the value appears in the array.
- If the entire array is traversed, the time complexity is O(N)
- If you look up this number in two, in general, O(logN)+O(1), in the worst case, the time complexity is O(1)+ O(N)
- Binary search the leftmost value and rightmost value of the value. The time complexity is 2*O(logN)
public class Main { public static int theNumOfK(int[] arr, int k) { if (arr == null || arr.length == 0) { return 0; } int first = getFirstK(arr, k); int last = getLastK(arr, k); if (first != -1 && last != -1) { return last - first + 1; } return 0; } private static int getFirstK(int[] arr, int k) { int start = 0; int end = arr.length - 1; while (start <= end) { int med = (((end - start) >> 1) + start); if (arr[med] == k) { // If it is this value, judge whether it is the first one. If it is returned, otherwise end converges to the left if ((med > 0 && arr[med - 1] != k) || med == 0) { return med; } else { end = med - 1; } } else if (arr[med] > k) { end = med - 1; } else { start = med + 1; } } return -1; } private static int getLastK(int[] arr, int k) { int start = 0; int end = arr.length - 1; while (start <= end) { int med = (((end - start) >> 1) + start); if (arr[med] == k) { // If it is this value, judge whether it is the last one. If it is returned, otherwise start converges to the right if ((med < arr.length - 1 && arr[med + 1] != k) || med == arr.length - 1) { return med; } else { start = med + 1; } } else if (arr[med] > k) { end = med - 1; } else { start = med + 1; } } return -1; } }
Missing numbers from 0 to n-1
Title: an array of incrementing integers with a length of n-1. Each integer is unique and ranges from 0 to n-1. Then there must be a number between 0 and N-1 that does not exist in the array. Return the number.
- Obviously, there is a close relationship between the data and the subscript, which is either equal or 1 different, and is orderly. It is obvious to use the modified binary search.
public class Main { public static int process(int[] arr) { if (arr == null || arr.length == 0) { return -1; } int left = 0; int right = arr.length - 1; while (left <= right) { int med = right + ((right - left) >> 1); if (med == arr[med]) { left = med + 1; } else { // The result is the leftmost subscript value in a combination of different subscripts and values // Therefore, judge whether it is the leftmost position only when the subscript is different. if (med == 0 || arr[med - 1] == med - 1) { return med; } else {// Not the leftmost position, continue two points right = med - 1; } } } // There are two situations in the program: // -All subscripts and numbers are the same, and left goes directly to arr.length // -The input data does not meet the meaning of the question if (left == arr.length) { return arr.length; } // If it does not meet the meaning of the question return -1; } }
An element in an array whose value and subscript are equal
Title: suppose that each element array in a monotonically increasing array is unique, implement a function, give an array of this type, and find out the elements of any array equal to the subscript.
- If the value is smaller than the subscript, the left bound does not exist
- If the value is larger than the subscript, the right definition does not exist
public class Main { public static int process(int[] arr) { if (arr == null || arr.length == 0) { return -1; } int left = 0; int right = arr.length - 1; while (left <= right) { int med = right + ((right - left) >> 1); if (med == arr[med]) { return med; } if (med < arr[med]) { right = med - 1; } else { left = med + 1; } } return -1; } }
Maximum depth of binary tree
Title: given a root node, return the maximum depth of binary tree.
public class Main { public static int maxDepth(Node root) { if (root==null) { return 0; } return Math.max(maxDepth(root.leftNode),maxDepth(root.rightNode))+1; } public static class Node { int value; Node leftNode; Node rightNode; } }
balanced binary tree
Title: given a root node, judge whether it is a balanced binary tree.
Tree class recursion idea:
public class Main { public static Data isAVL(Node head) { if (head == null) return new Data(0, true); Data lData = isAVL(head.left); Data rData = isAVL(head.right); int height = Math.max(lData.height, rData.height) + 1; boolean flag = lData.isAVL && rData.isAVL && Math.abs(lData.height - rData.height) <= 1; return new Data(height, flag); } static class Data { int height; boolean isAVL; public Data(int height, boolean is) { this.height = height; this.isAVL = is; } } }
A number that appears only once in the array π
Title: given an integer array, only two numbers appear once, and other numbers appear even times. Output the number of these two numbers once.
- According to the difference of two values, there must be different binary bits. Therefore, the array is divided into two parts, which is transformed into finding the value with and only one value in the array once.
public class Main { public static void twoSingleNum(int[] arr) { int med = 0; for (int a : arr) { med ^= a;// Two different singular numbers ^ finally get med } int rightOne = med & (~med + 1);// Take out the bit value with binary 1 in med (it must exist because of different values) int med1 = 0; for (int a : arr) { // Take out the value with the corresponding bit of 1 for ^ the last to the value with the corresponding bit of 1 in the two singular numbers // (A & rightone) = = 0, the corresponding bit is 0 if ((a & rightOne) == rightOne) { med1 ^= a; } } System.out.println(med1);// One of two singular values System.out.println(med ^ med1);// Two singular numbers make one value } }
The only number in the array that appears only once π
Title: given an integer array, only one number appears once, other numbers appear three times, and output this number that appears only once.
- If you look at the numbers only three times, the sum of the binary bits of these values can be divided by 3
- Now there is a number that appears only once. Due to the existence of the number, the remainder of the sum of all binary bits divided by 3 is the corresponding binary bit of the value.
public class Main { public static int twoSingleNum(int[] arr) { int[] bit = new int[32];// Sum every bit for (int a : arr) { int b = 1; for (int i = 31; i >= 0; --i) { if ((a & b) != 0) {// If it is 1, it will accumulate ++bit[i]; } b <<= 1;// transposition } } int res = 0; for (int i = 0; i < 32; ++i) { res = res << 1; res += (bit[i] % 3);// Remainder } return res; } }
And are two numbers of s
Title: given an integer increasing array and integer s, find two values in the array and add them to S. if they exist, they return true and if they do not exist, they return false.
public class Main { public static boolean process(int[] arr, int s) { if (arr == null || arr.length < 2) { return false; } int left = 0; int right = arr.length - 1; while (left < right) { int sum = arr[left] + arr[right]; if (sum == s) { return true; } else if (sum > s) { --right; } else { ++left; } } return false; } }
Continuous positive sequence with sum s
Title: given an integer s, print all sequences of continuous positive numbers with sum s. For example, if you enter 15, there is 1 + 2 + 3 + 4 + 5 = 4 + 5 + 6 = 7 + 8 = 15
public class Main { public static void process(int s) { if (s < 3) { return; } int small = 1; int big = 2; int curSum = small + big; while (small <= s / 2) { if (curSum == s) { Printer(small, big); curSum -= small; ++small; } else if (curSum < s) { ++big; curSum += big; } else { curSum -= small; ++small; } } } private static void Printer(int small, int big) { for (int i = small; i <= big; i++) { System.out.print(i + " "); } System.out.println(); } }
Reverse string π
Title: enter an English sentence and return the inversion of the word, for example: "I am a pig", return "pig a am I"“
- Overall reverse order and then individual reverse order. "I am a pig" -- > "gip a ma I" -- > "pig a am I"“
public class Main { public static String process(String s) { if (s == null || s.length() == 0) { return null; } char[] arr = s.toCharArray(); reverse(arr, 0, arr.length - 1); int begin = 0; int end = 0; while (begin < arr.length) { if (end == arr.length || arr[end] == ' ') { // Both arr[begin] and arr[end-1] are characters reverse(arr, begin, end - 1); // Let begin point to the first of the next word or exit out of bounds // end points to the first of the next word and continues walking backwards begin = ++end; } else { ++end; } } return new String(arr); } private static void reverse(char[] arr, int left, int right) { if (left >= right) { return; } while (left < right) { char temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; ++left; --right; } } }
Left rotation string π
Title: given a string and an integer k, return the left rotation string, for example: k=3 "abcdefg" - > "defgabc"
- "abcdefg" --> "cbagfed" --> "defgabc"
public class Main { public static String leftReverseString(String s,int k) { if (s==null||s.length()==0) { return null; } char[] arr=s.toCharArray(); reverse(arr, 0, k-1); reverse(arr, k, arr.length-1); reverse(arr, 0, arr.length-1); return new String(arr); } private static void reverse(char[] arr, int left, int right) { if (left >= right) { return; } while (left < right) { char temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; ++left; --right; } } }
Maximum value of the queue (double ended queue)
Title: given the size of an array and sliding window, please find the maximum value in all sliding windows. For example, if you enter the array {2,3,4,2,6,2,5,1} and the size of sliding window 3, there are six sliding windows, and their maximum values are {4,4,6,6,5}
- The double ended queue stores the index so that the data can be uniquely determined
- And the size of the corresponding value is strictly in descending order in the double ended queue
public class Main { public static List<Integer> process(int[] arr, int windowLen) { if (arr == null || arr.length == 0 || windowLen <= 0) { return null; } // Instantiate Dual Ended queue ArrayList<Integer> maxDownList = new ArrayList<Integer>(); // First fill in the number of windowLen before arr according to the rules for (int i = 0; i < windowLen; i++) { while (!maxDownList.isEmpty() && arr[i] >= arr[maxDownList.get(maxDownList.size() - 1)]) { maxDownList.remove(maxDownList.size() - 1); } maxDownList.add(i);// The index is filled in for unique identification } // Maximum storage List<Integer> list = new ArrayList<>(); list.add(arr[maxDownList.get(0)]); for (int i = windowLen; i < arr.length; ++i) { // Keep the data in the arr corresponding to the index in the double ended queue in descending order. while (!maxDownList.isEmpty() && arr[i] >= arr[maxDownList.get(maxDownList.size() - 1)]) { maxDownList.remove(maxDownList.size() - 1); } // Is the first element of the current double ended queue still in the sliding window if (!maxDownList.isEmpty() && maxDownList.get(0) <= i - windowLen) { maxDownList.remove(0); } // Fill in the data at this time maxDownList.add(i); // Record maximum list.add(arr[maxDownList.get(0)]); } return list; } }
Abstract modeling capability
Joseph Ring π
public class Test { /** * @param N Total number of current elements * @param index The first death * @return The position of the last surviving person (non index) which is the current position in the round */ public static int getLive(int N, int index) { if (N == 1) return 1;//This 1 represents the index of this new round when there is one left // Ranking of survivors in the previous round = (ranking of survivors in this round + index - 1)% N + 1 return (getLive(N - 1, index) + index - 1) % N + 1; } }
Maximum profit of stock
Title: suppose that the price of a stock is stored in the array in chronological order, what is the maximum profit that can be obtained by buying and selling the stock at one time? For example, the price of a stock at a certain time node is {9,1,8,5,7,12,16,14). If we can buy when the price is 5 and sell when the price is 16, we can gain the maximum profit 11.
public class Main { public static int process(int[] arr) { if (arr==null||arr.length<2) { return 0; } // Minimum value of previous occurrence int min=arr[0]; // Maximum difference before int maxSub=arr[1]-arr[0]; for (int i = 1; i < arr.length; i++) { maxSub=Math.max(maxSub, arr[i]-min); // Update after calculating the difference, because the minimum value arr[i] at this time cannot be reduced by itself min=Math.min(min,arr[i]); } return maxSub; } }