[LeetCode] Introduction to data structures and algorithms - day 1

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.

Posted on Sun, 05 Dec 2021 07:50:31 -0500 by lynn897