There are no major events recently. Come and have a look at the data structure and write questions to activate your mental thinking.
In computer science, data structure is a way of storing and organizing data in computer. The data structure and algorithm are abstract, so it still needs some perseverance to learn. Broadly speaking, data structure refers to the storage structure of a group of data, and algorithm is a group of methods to operate data.
Algorithms and data structures complement each other. The data structure serves the algorithm, and the algorithm should act on the specific data structure.
*Topic from: introduction to LeetCode data structure

217. There are duplicate elements
Some special test cases:
- [1, 2, 3, 1]
- [1, 1, 1, 3, 3, 4, 3, 2, 4, 2]
- [1, 2, 3, 4]
- [0]
- [7, 3, 2, 1, 2]
- [1, 5, -2, -4, 0]
- [2, 14, 18, 22, 22]
At present, personal thoughts can be divided into three different directions:
- Direction 1: do not use the convenience API provided by arrays
- Except for basic operations (push, pop, unshift, shift, slice, splice)
- Direction 2: you can use all the API s provided by the array
- Direction 3: other skills
*Note: the following code can be rewritten with for
01 - direction I
1. Double circulation method
1.1 violence
function containsDuplicate(nums: number[]): boolean { let i = 0 let j = 0 while (i < nums.length) { // n const item = nums[i] while (j < nums.length) { // n if (item === nums[j]) {return true} j++ } i++ } return false };
Features: all elements participate in comparative judgment, including themselves
Time complexity: O(n^2)
Question: the comparison between yourself and yourself will be directly true
Result: not applicable
1.2 optimization
function containsDuplicate(nums: number[]): boolean { let i = 0 let j = i + 1 while (i < nums.length) { // n const item = nums[i] while (j < nums.length) { // n - 1 if (item === nums[j]) { return true } j++ } i++ j = i + 1 } return false };
Features: each cycle is only compared with itself and elements outside history
Time complexity: on (n * (n - 1)) = > O (n ^ 2)
Problem: because of the order, if the repeated elements are at the end, it will take too much time to make invalid comparison
Results: it can be realized, but it takes more time
2. Double pointer
2.1 use space
function containsDuplicate(nums: number[]): boolean { const arr = [] const findNum = (num: number): boolean => { let kl = 0 let kr = arr.length - 1 while (kl <= kr) { if (num === arr[kl] || num === arr[kr]) { return true } kl++ kr-- } return false } let i = 0 let j = nums.length - 1 while (i <= j) { // n / 2 const l = nums[i] const r = nums[j] if (i === j && findNum(l)) { return true } // n / 2 + 1 if (l === r) { return true } if (findNum(l)) { return true } // n / 2 + 1 if (findNum(r)) { return true } // n / 2 + 1 arr.push(l) arr.push(r) i++ j-- } return false };
Features: the outer layer only circulates n / 2 times, but additional space is required to record historical data. It is still a double cycle in essence
Time complexity: O (3m * n) = > O (MN)
Problem: you need additional space to record the traversed values, and you need to traverse the history again. You need to deal with the boundary conditions of many cases
Results: it can be realized, but the implementation can be logically optimized without additional space
2.2 optimization without space
function containsDuplicate(nums: number[]): boolean { let i = 0 let j = nums.length - 1 while (i <= j) { // n const l = nums[i] const r = nums[j] if (l === r && i !== j) return true let ci = i + 1 let cj = j - 1 while (ci <= cj) { // n if (l === nums[ci] || l === nums[cj]) return true if (r === nums[ci] || r === nums[cj]) return true ci++ cj-- } i++ j-- } return false };
Features: Double pointers are used inside and outside, but it is still double circulation in essence, but the boundary conditions are greatly reduced
Time complexity: O (n * n) = > O (n ^ 2)
Problem: although it no longer takes up additional space and the inner loop uses double pointers, it does not change its complexity
Results: it can be realized without changing its complexity
02 - direction II
1. Use indexOf
function containsDuplicate(nums: number[]): boolean { for (let i = 0; i < nums.length; i++) { // n if (nums.indexOf(nums[i], i + 1) !== -1) return true // n } return false };
Features: simple, intuitive and easy to understand
Time complexity: O (n * n) = > O (n ^ 2)
Problem: Although the indexOf API is used to implement it, it looks like O(1), but it is still O(n) inside
Result: achievable
2. Use sort
function containsDuplicate(nums: number[]): boolean { if (!nums || !nums.length) return false nums = nums.sort() for (let i = 0; i < nums.length; i++) { if (nums[i] === nums[i + 1]) return true } return false };
Features: after sorting, the same elements will be adjacent. You can compare the current element with the next element each time
Time complexity: O (n + n) = > O (n)
Problem: single type elements are feasible. If complex types occur, more situations need to be considered
Result: achievable
03 - Fang Xiangsan
1. Use object key
function containsDuplicate(nums: number[]): boolean { if (!nums || !nums.length) return false const obj: { [key: string]: number } = {} for (let i = 0; i < nums.length; i++) { if (obj[nums[i]]) { return true } if (!obj[nums[i]]) { obj[nums[i]] = 1 } } return false };
Features: use the non repeatable key feature of the object. If you encounter an existing key, you can confirm that there are duplicate elements
Time complexity: O(n)
Problem: it uses data type characteristics, not algorithm implementation
Result: achievable
2. Use Set to create new data types
function containsDuplicate(nums: number[]): boolean { if (!nums || !nums.length) return false const nNums = new Set(nums) return nums.length > nNums.size };
Features: using the natural de duplication feature of Set, we can directly draw a conclusion through the comparison of array length
Time complexity: O(1)
Problem: it uses data type characteristics, not algorithm implementation
Result: achievable
3. Use Map to create new data types
function containsDuplicate(nums: number[]): boolean { if (!nums || !nums.length) return false const nNums = new Map() for (let i = 0; i < nums.length; i++) { if (nNums.has(nums[i])) { return true } else { nNums.set(nums[i], 1) } } return false };
Features: using the characteristics of Map data structure, its principle is similar to that of object
Time complexity: O(n)
Problem: it uses data type characteristics, not algorithm implementation
Result: achievable
last
Give labels according to the meaning of the question: array, hash table and sorting. Among the above implementation methods, only two, two, three, one, three, two, three and three can meet the meaning of the question and are better.
Learning is anti human and relatively painful for most people. The sense of pleasure and achievement after learning is incomparable. But the process is difficult and boring.
Title Source:
Introduction to LeetCode data structure: https://leetcode-cn.com/study-plan/data-structures
Copyright notice:
The copyright of this article belongs to the author Lin Xiaoshuai. It cannot be reproduced or modified without authorization.
For reprint or cooperation, please leave a message and contact information in the background.