Given an array of integers called nums, sort the array in non-decreasing order using the quick sort algorithm and return the sorted array.
A sorted array in non-decreasing order is an array where each element is greater than or equal to all preceding elements in the array.
Input: nums = [7, 4, 1, 5, 3]
Output: [1, 3, 4, 5, 7]
Explanation: 1 <= 3 <= 4 <= 5 <= 7.
Thus the array is sorted in non-decreasing order.
Input: nums = [5, 4, 4, 1, 1]
Output: [1, 1, 4, 4, 5]
Explanation: 1 <= 1 <= 4 <= 4 <= 5.
Thus the array is sorted in non-decreasing order.
Input: nums = [3, 2, 3, 4, 5]
Quick Sort is a divide-and-conquer algorithm like Merge Sort. However, unlike Merge Sort, Quick Sort does not use an extra array for sorting (though it uses an auxiliary stack space). This makes Quick Sort slightly better than Merge Sort from a space perspective.
This algorithm follows two simple steps repeatedly:
To summarize: The main goal is to place the pivot at its final position in each recursion call, where it should be in the final sorted array.
To implement Quick Sort, we will create two functions: quickSort()
and partition()
.
quickSort(arr[], low, high)
partition()
function to get the index where the pivot should be placed after sorting. This index, called the partition index, separates the left and right unsorted subarrays.quickSort()
for the left and right subarrays. The range of the left subarray will be [low to partition index - 1] and the range of the right subarray will be [partition index + 1 to high].partition(arr[], low, high)
arr[low]
).i
(low) and j
(high). Move i
forward to find element > pivot, and j
backward to find element < pivot. Ensure i <= high - 1
and j >= low + 1
.i < j
, swap arr[i]
and arr[j]
.j < i
.arr[low]
) with arr[j]
and return j
as partition index.This approach ensures that Quick Sort efficiently sorts the array using the divide-and-conquer strategy.
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
// Function to partition the array
int partition(vector<int>& arr, int low, int high) {
// Choosing the first element as pivot
int pivot = arr[low];
// Starting index for left subarray
int i = low;
// Starting index for right subarray
int j = high;
while (i < j) {
/* Move i to the right until we find an
element greater than the pivot */
while (arr[i] <= pivot && i <= high - 1) {
i++;
}
/* Move j to the left until we find an
element smaller than the pivot */
while (arr[j] > pivot && j >= low + 1) {
j--;
}
/* Swap elements at i and j if i is still
less than j */
if (i < j) swap(arr[i], arr[j]);
}
// Pivot placed in correct position
swap(arr[low], arr[j]);
return j;
}
// Helper Function to perform the recursive quick sort
void quickSortHelper(vector<int>& arr, int low, int high) {
/* Base case: If the array has one or no
elements, it's already sorted */
if (low < high) {
// Get the partition index
int pIndex = partition(arr, low, high);
// Sort the left subarray
quickSortHelper(arr, low, pIndex - 1);
// Sort the right subarray
quickSortHelper(arr, pIndex + 1, high);
}
}
// Function to perform quick sort on given array
vector<int> quickSort(vector<int>& nums) {
// Get the size of array
int n = nums.size();
// Perfrom quick sort
quickSortHelper(nums, 0, n-1);
// Return sorted array
return nums;
}
};
int main() {
vector<int> arr = {4, 6, 2, 5, 7, 9, 1, 3};
int n = arr.size();
cout << "Before Sorting Array: " << endl;
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
// Create an instance of Solution class
Solution solution;
// Function call to sort the array using quick sort
vector<int> sortedArr = solution.quickSort(arr);
cout << "After Sorting Array: " << endl;
for (int i = 0; i < n; i++) {
cout << sortedArr[i] << " ";
}
cout << endl;
return 0;
}
import java.util.*;
class Solution {
// Function to partition the array
public int partition(int[] arr, int low, int high) {
// Choosing the first element as pivot
int pivot = arr[low];
// Starting index for left subarray
int i = low;
// Starting index for right subarray
int j = high;
while (i < j) {
/* Move i to the right until we find an
element greater than the pivot */
while (arr[i] <= pivot && i <= high - 1) {
i++;
}
/* Move j to the left until we find an
element smaller than the pivot */
while (arr[j] > pivot && j >= low + 1) {
j--;
}
/* Swap elements at i and j if i is still
less than j */
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// Pivot placed in correct position
int temp = arr[low];
arr[low] = arr[j];
arr[j] = temp;
return j;
}
// Helper Function to perform the recursive quick sort
public void quickSortHelper(int[] arr, int low, int high) {
/* Base case: If the array has one or no
elements, it's already sorted */
if (low < high) {
// Get the partition index
int pIndex = partition(arr, low, high);
// Sort the left subarray
quickSortHelper(arr, low, pIndex - 1);
// Sort the right subarray
quickSortHelper(arr, pIndex + 1, high);
}
}
// Function to perform quick sort on given array
public int[] quickSort(int[] nums) {
// Get the size of array
int n = nums.length;
// Perform quick sort
quickSortHelper(nums, 0, n - 1);
// Return sorted array
return nums;
}
}
public class Main {
public static void main(String[] args) {
int[] arr = {4, 6, 2, 5, 7, 9, 1, 3};
int n = arr.length;
System.out.println("Before Sorting Array:");
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
// Create an instance of Solution class
Solution solution = new Solution();
// Function call to sort the array using quick sort
int[] sortedArr = solution.quickSort(arr);
System.out.println("After Sorting Array:");
for (int i = 0; i < n; i++) {
System.out.print(sortedArr[i] + " ");
}
System.out.println();
}
}
class Solution:
# Function to partition the array
def partition(self, arr, low, high):
# Choosing the first element as pivot
pivot = arr[low]
# Starting index for left subarray
i = low
# Starting index for right subarray
j = high
while i < j:
# Move i to the right until we find an
# element greater than the pivot
while arr[i] <= pivot and i <= high - 1:
i += 1
# Move j to the left until we find an
# element smaller than the pivot
while arr[j] > pivot and j >= low + 1:
j -= 1
# Swap elements at i and j if i is still
# less than j
if i < j:
arr[i], arr[j] = arr[j], arr[i]
# Pivot placed in correct position
arr[low], arr[j] = arr[j], arr[low]
return j
# Helper Function to perform the recursive quick sort
def quickSortHelper(self, arr, low, high):
# Base case: If the array has one or no
# elements, it's already sorted
if low < high:
# Get the partition index
pIndex = self.partition(arr, low, high)
# Sort the left subarray
self.quickSortHelper(arr, low, pIndex - 1)
# Sort the right subarray
self.quickSortHelper(arr, pIndex + 1, high)
# Function to perform quick sort on given array
def quickSort(self, nums):
# Get the size of array
n = len(nums)
# Perform quick sort
self.quickSortHelper(nums, 0, n - 1)
# Return sorted array
return nums
if __name__ == "__main__":
arr = [4, 6, 2, 5, 7, 9, 1, 3]
n = len(arr)
print("Before Sorting Array:")
for i in range(n):
print(arr[i], end=" ")
print()
# Create an instance of the Solution class
solution = Solution()
# Function call to sort the array using quick sort
sortedArr = solution.quickSort(arr)
print("After Sorting Array:")
for i in range(n):
print(sortedArr[i], end=" ")
print()
class Solution {
// Function to partition the array
partition(arr, low, high) {
// Choosing the first element as pivot
let pivot = arr[low];
// Starting index for left subarray
let i = low;
// Starting index for right subarray
let j = high;
while (i < j) {
// Move i to the right until we find an
// element greater than the pivot
while (arr[i] <= pivot && i <= high - 1) {
i++;
}
// Move j to the left until we find an
// element smaller than the pivot
while (arr[j] > pivot && j >= low + 1) {
j--;
}
// Swap elements at i and j if i is still
// less than j
if (i < j) {
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
// Pivot placed in correct position
[arr[low], arr[j]] = [arr[j], arr[low]];
return j;
}
// Helper Function to perform the recursive quick sort
quickSortHelper(arr, low, high) {
// Base case: If the array has one or no
// elements, it's already sorted
if (low < high) {
// Get the partition index
let pIndex = this.partition(arr, low, high);
// Sort the left subarray
this.quickSortHelper(arr, low, pIndex - 1);
// Sort the right subarray
this.quickSortHelper(arr, pIndex + 1, high);
}
}
// Function to perform quick sort on given array
quickSort(nums) {
// Get the size of array
let n = nums.length;
// Perform quick sort
this.quickSortHelper(nums, 0, n - 1);
// Return sorted array
return nums;
}
}
const main = () => {
let arr = [4, 6, 2, 5, 7, 9, 1, 3];
let n = arr.length;
console.log("Before Sorting Array:");
for (let i = 0; i < n; i++) {
process.stdout.write(arr[i] + " ");
}
console.log();
// Create an instance of Solution class
let solution = new Solution();
// Function call to sort the array using quick sort
let sortedArr = solution.quickSort(arr);
console.log("After Sorting Array:");
for (let i = 0; i < n; i++) {
process.stdout.write(sortedArr[i] + " ");
}
console.log();
}
// Execute the main function
main();
Time Complexity: O(N*logN), where N = size of the array. At each step, we divide the whole array, which takes logN steps, and n steps are taken for the partition() function, so overall time complexity will be N*logN.
The following recurrence relation can be written for Quick sort:
F(n) = F(k) + F(n-1-k)
Here, k is the number of elements smaller or equal to the pivot and n-1-k denotes elements greater than the pivot.
There can be 2 cases:
Worst Case: This case occurs when the pivot is the greatest or smallest element of the array. If the partition is done and the last element is the pivot, then the worst case would be either in the increasing order of the array or in the decreasing order of the array.
Recurrence:
F(n) = F(0) + F(n-1)
or F(n) = F(n-1) + F(0)
Worst Case Time Complexity: O(n2)
Best Case: This case occurs when the pivot is the middle element or near to middle element of the array.
Recurrence:
F(n) = 2F(n/2)
Time Complexity for the best and average case: O(N*logN)
Space Complexity: O(1) + O(N) auxiliary stack space, where N = size of the array.
Q: How does quick sort ensure efficiency for large datasets?
A: Quick sort ensures efficiency by: Dividing the array into smaller subproblems. Recursively sorting these subproblems with O(nlogn) complexity on average. Being cache-friendly due to in-place operations, reducing memory overhead. Its performance depends on balanced partitioning, which is achieved through good pivot selection strategies like randomization or median-of-three.
Q: How does quick sort partition the array, and why is it critical?
A: Partitioning is the core of quick sort. It rearranges the array so that all elements smaller than the pivot appear before it, and all larger elements appear after it. This ensures the pivot is placed in its correct position. The process enables quick sort to recursively sort smaller subarrays. Example: Input: [4, 2, 9, 1] with pivot = 4. Partitioning results in: [2, 1], [4], [9]. Now the pivot (4) is correctly positioned in the sorted array.
Q: Can quick sort be made stable?
A: Quick sort is inherently unstable because the partitioning step involves swapping elements, which can disrupt the relative order of duplicate elements. Use extra memory to track original indices. Modify the partitioning process to preserve the order of equal elements.
Q: How does quick sort partition the array, and why is it critical?
A: Partitioning is the core of quick sort. It rearranges the array so that all elements smaller than the pivot appear before it, and all larger elements appear after it. This ensures the pivot is placed in its correct position. The process enables quick sort to recursively sort smaller subarrays. Example: Input: [4, 2, 9, 1] with pivot = 4. Partitioning results in: [2, 1], [4], [9]. Now the pivot (4) is correctly positioned in the sorted array.