Техника сортировки, о которой мало говорят. Тем не менее, это помогает решить некоторые важные вопросы оптимизированным способом. Для лучшего понимания необходимо знание основ.

Алгоритм сортировки на месте, помогающий свести к минимуму количество операций записи в память. Этот алгоритм не требует дополнительного места. Весь обмен происходит в виде циклов. Каждое значение либо записывается ноль раз, если оно уже находится в правильном положении, либо записывается один раз в правильное положение.

Каждый цикл состоит из размещения элемента в его отсортированном индексе, взятия элемента в этом отсортированном индексе и размещения его в его отсортированном индексе, и повторяется до тех пор, пока массив не будет отсортирован.

Его можно рассматривать как граф, где узлы соединены с ребрами. Чтобы узел A и узел B были связаны, чтобы отсортировать массив, элементы в узле A должны быть в индексе узла B при вращении.

Этот подход весьма полезен при работе с числами в заданном диапазоне и поиске дубликатов/отсутствующих и т. д.

Идея алгоритма состоит в том, чтобы поместить элементы в их правильный индекс.

Идея алгоритма.

Предположим, есть массив arr, содержащий n различных элементов. Имея элемент A, мы можем найти его индекс, подсчитав количество элементов, меньших, чем A.

  1. Если элемент находится в правильном положении, просто оставьте его как есть.
  2. В противном случае нам нужно найти правильное положение A, подсчитав количество меньших элементов. Другой элемент B заменяется и перемещается в правильное положение. Этот процесс продолжается до тех пор, пока мы не получим элемент в исходной позиции A.

Код.

Важное примечание

Циклическая сортировкаиспользуется, когда элементы в массиве находятся в диапазоне от 1 до n, где n — размер массива.

В некоторых вопросах элементы могут быть в диапазоне от 0 до n (n = размер массива), поэтому мы подойдем к вопросу соответствующим образом, но логика и идея подхода к вопросу останутся прежними. Мы по-прежнему могли бы легко применить этот алгоритм.

ПРОБЛЕМЫ

  1. Отсутствует номер


// T.C. - O(N*logN), S.C. - O(1) .
int missingNumber(vector<int>& nums) {
    
        sort(nums.begin(), nums.end());
        int start = 0, end = nums.size() - 1;
        // going to apply Binary Search
        while(start <= end) {
            // avoiding overflow
            int mid = start + (end - start)/2;
            
            if(nums[mid] > mid) 
                end = mid - 1;
            else 
                start = mid + 1;
        }
        return start;
    }

Прежде чем узнать о циклической сортировке, я решил этот вопрос с помощью алгоритма Sorting + Binary Search. Но для этого потребовалась только временная сложность O (N * log N) и пространственная сложность O (1), поскольку мы не используем никакого дополнительного вспомогательного пространства.

Но чтобы оптимизировать временную сложность до O(N), мы можем использовать циклическую сортировку, поскольку у нас есть числа от 0 до N, мы можем разместить элементы по их правильному индексу.

Как только мы получим каждое число в правильном месте, мы можем просто перебрать весь массив и найти пропущенное число.

//  cyclic sort , T.C. - O(N), S.C. - O(1) .
int missingNumber(vector<int>& nums) {
        int i = 0;
        int n = nums.size();
        while(i < n) {
        int correct = nums[i]; //where this element should be in sorted array
            if(correct < n && correct != i) {
                swap(nums[i], nums[correct]); //put current element at correct position
            } 
            else
            i++; // move to next index
        }
        for(int i = 0; i < n ; i++) {
            if(nums[i] != i) {
                return i;
             }
        }
        return n;
    } 

2. Первый отсутствующий положительный результат



int firstMissingPositive(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int cnt = 1;
        for(int i = 0; i< nums.size(); i++) {
            if(nums[i] > 0 && nums[i] == cnt) {
                cnt++;
            }
        }
        return cnt;
    }

Первоначально счет может быть инициализирован с 1 (наименьшее положительное число). Затем нам нужно определить, где количество не соответствует числовым значениям, потому что решение — когда оно не равно.

Это наивное решение, поскольку интуиция состоит в том, чтобы сортировать и находить, но временная сложность составляет O (N * logN). Мы также можем использовать алгоритм бинарного поиска, но это не улучшит временную сложность.

Для дальнейшей оптимизации мы будем использовать циклическую сортировку. После его применения мы можем просто проверить, какой первый элемент в массиве nums находится не на своем месте.

int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        // Using Cyclic Sort Technique
        for(int i = 0; i < n; ) {
        // ignoring the negative & >n numbers, 
        // as the 1st missing number would always lie between 1 to n 
          if(nums[i] > 0 && nums[i] < nums.size() && nums[nums[i] - 1] != nums[i]) 
                swap(nums[nums[i] - 1], nums[i]);
            else 
                i++;
        }
        // run a loop to check which is the 1st element, not at the correct index after sorting
        for (int i = 0; i < n; i++) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        return n + 1;
    }

3.Найдите повторяющийся номер

Можно применить множество методов для ее решения. Я буду обсуждать лишь некоторые из них. Наивный подход состоит в том, чтобы использовать два цикла for для решения этой задачи в T.C.-O(N²). и СК-О(1).



       // T.C.-O(N*logN), S.C.-O(1)
        int findDuplicate(vector<int>& nums) {
        vector<int> res;
        sort(nums.begin(), nums.end());
        
        for(int i = 0; i < nums.size(); i++) {
            if(nums[i] == nums[i + 1]) { 
               return nums[i];
             }
          }
        return -1;
     }

Другой подход, который должен прийти на ум, заключается в сортировке несортированного массива таким образом, чтобы все повторяющиеся элементы располагались рядом друг с другом. Затем пройдитесь по массиву, чтобы найти повторяющийся элемент, и, как только он будет найден, верните первый дубликат.

Мы также можем воспользоваться помощью хеш-карты, это уменьшит временную сложность до O(N), но пространственная сложность тоже будет O(N), что не очень хорошо. Ну, это подход, который пришел мне в голову, когда я прочитал вопрос. Хэш-карта широко используется во многих вопросах.

     // T.C.-O(N), S.C.-O(N)
     int findDuplicate(vector<int>& nums) {
     unordered_map<int, int> mp;
     for(auto i:nums) mp[i]++;
     int ans;
     for(auto j : mp) {
        if(j.second >= 2) {
             ans = j.first;
          }
       }
     return ans;
   }

Используя циклическую сортировку, мы решаем этот вопрос в T.C.-O(N) и S.C.-O(1).

 int findDuplicate(vector<int>& arr){
        // cyclic sort, T.C. - O(N), S.C. - O(1)
        int n = arr.size();
        for(int i = 0; i < n; i++) {
            while(arr[i] != i + 1 && arr[arr[i] - 1] != arr[i]) {
                swap(arr[i], arr[arr[i] - 1]);
            }
        }
        for(int i = 0;i < n; i++) {
            if(i + 1 != arr[i]) 
                return arr[i];
        }
        return -1;
    }

4. Найти все дубликаты в массиве



 // cyclic sort. T.C. - O(N), S.C. - O(1)
    vector<int> findDuplicates(vector<int>& nums) {
    int n = nums.size();
    int i = 0;
    while(i < n) {
        int correct = nums[i] - 1;
        if (nums[i] != nums[correct]) {
            swap(nums[i], nums[correct]);
        }
        else {
            i++;
        }
    }
    vector<int> res;
    for(int i = 0;i < n; i++) {
        if(nums[i] != i + 1) {
            res.push_back(nums[i]);
        }
    }
    return res;
}

Этот вопрос также можно решить с помощью различных методов. Но использование циклической сортировки дает наиболее оптимизированное решение.

5. Найти все числа, исчезнувшие в массиве



 vector<int> findDisappearedNumbers(vector<int>& nums) {    
        int n = nums.size(); 
        int i = 0;
        // cyclic sort . T.C.- O(N), S.C.- O(1).
        while(i < n) {
            int correct = nums[i] - 1;
            if(nums[i] != nums[correct]) {
                swap(nums[i], nums[correct]);
            } else {
                i++;
            }
        }
        vector<int> res;
        for(int i = 0; i < n; i++) {
            if(nums[i] != i + 1)
                res.push_back(i + 1);
        }
        return res;
    }

Решите и эти проблемы.

1. Поврежденная пара



2. Перемешать строку



3. Установить несоответствие



ЗАКЛЮЧЕНИЕ

Изучая циклическую сортировку несколько месяцев назад, я столкнулся со многими трудностями в ее понимании. Я просмотрел много видео и документации, чтобы понять это. Вы должны изучить как можно больше об этом самостоятельно. Я укрепил свои знания об этом через этот блог. Я надеюсь, что это поможет вам. Если в какой-то части есть ошибка, скажите, исправлю.