데이터 구조 및 알고리즘 퀵 정렬

퀵 정렬(Quick Sort) 은 분할 정복 방식을 기반으로 한 정렬 알고리즘입니다. 배열을 더 작은 하위 배열로 지속적으로 나누고 각 하위 배열을 재귀적으로 정렬하여 결국 전체 배열을 정렬하는 방식으로 작동합니다.

递归
递归
确定枢轴元素
将小于枢轴的元素放在枢轴的左边
将大于枢轴的元素放在枢轴的右边
对左子数组进行快速排序
对右子数组进行快速排序

퀵 정렬 알고리즘의 원리는 다음과 같습니다.

  1. 피벗 요소로 정렬할 요소를 배열에서 선택합니다.
  2. 피벗 요소보다 작은 모든 요소를 ​​피벗 요소의 왼쪽으로 이동하고, 피벗 요소보다 큰 모든 요소를 ​​피벗 요소의 오른쪽으로 이동합니다.
  3. 빠른 정렬 알고리즘은 하위 배열에 요소가 하나만 남거나 비어 있을 때까지 왼쪽 하위 배열과 오른쪽 하위 배열에서 반복적으로 호출됩니다.
  4. 재귀가 끝나면 전체 배열이 정렬됩니다.

지속적으로 피벗 요소를 선택하고 배열을 분할함으로써 퀵 정렬 알고리즘은 전체 배열이 최종적으로 정렬될 때까지 배열을 점점 더 작은 하위 배열로 나눕니다. 이 알고리즘의 시간복잡도는 이다 O(nlogn).

이중 포인터 방법은 일반적으로 기본 요소의 올바른 위치를 찾는 데 사용됩니다. 먼저, 왼쪽 포인터는 배열의 시작 부분을 가리키고 오른쪽 포인터는 배열의 끝 부분을 가리킵니다. 그런 다음 왼쪽 포인터는 기본 요소보다 큰 요소를 찾아 멈출 때까지 왼쪽에서 오른쪽으로 이동합니다. 오른쪽 포인터는 기본 요소보다 작은 요소를 찾아 멈출 때까지 오른쪽에서 왼쪽으로 이동합니다. 다음으로 왼쪽 포인터와 오른쪽 포인터가 가리키는 요소를 바꿉니다. 왼쪽 포인터와 오른쪽 포인터가 만날 때까지 위 단계를 반복합니다. 마지막으로 기본 요소를 왼쪽 포인터가 가리키는 요소와 교환하여 기본 요소가 올바른 위치에 있도록 합니다.

구체적인 구현은 다음과 같습니다.

// 定义一个交换函数,用于交换数组中的两个元素
function swap(arr, i, j) {
    
    
  [arr[i], arr[j]] = [arr[j], arr[i]];
}

// 定义一个分区函数,用于对数组进行分区操作
function partition(arr, left, right) {
    
    
  const pivot = arr[right - 1]; // 将分区点设置为最右边的元素
  let i = left,
    j = right - 1; // 初始化指针i和j
  while (i !== j) {
    
    
    // 当i和j指针不相遇时,进行循环
    arr[i] <= pivot ? i++ : swap(arr, i, --j); // 如果arr[i]小于等于分区点,则i指针向右移动;否则进行交换,并将j指针向左移动
  }
  swap(arr, j, right - 1); // 将分区点放置在正确的位置
  return j; // 返回分区点的索引
}

// 定义一个快速排序函数
function qsort(arr, left = 0, right = arr.length) {
    
    
  if (right - left <= 1) return; // 当要排序的元素个数小于等于1时,直接返回
  const p = partition(arr, left, right); // 进行分区操作,并获取分区点的索引p
  qsort(arr, left, p); // 对分区点左侧的子数组进行快速排序
  qsort(arr, p + 1, right); // 对分区点右侧的子数组进行快速排序
}

const arr = [5, 3, 8, 4, 2, 1, 10, 11, -4, 55];
qsort(arr); // [ -4, 1, 2, 3, 4, 5, 8, 10, 11, 55 ]
console.log(arr);

기타 구현 방법:

/** 
1. 第一段程序使用的是递归的方式实现快速排序。它选择数组中间的元素作为基准元素,然后将数组分割为左右两个子数组,将比基准元素小的元素放在左子数组,将比基准元素大的元素放在右子数组,然后递归地对左右两个子数组进行快速排序,最后将左右两个子数组和基准元素拼接起来作为排序结果。

2. 第二段程序使用的是基于指针的方式实现快速排序。它通过定义一个分区函数来选择基准元素,并将数组分割为左右两个子数组,然后再递归地对左右两个子数组进行快速排序。分区函数中使用了双指针的方法,从左到右找到第一个大于基准元素的元素,从右到左找到第一个小于基准元素的元素,然后交换它们的位置。这样,最终基准元素的位置就确定下来了,并且左边的元素都小于等于基准元素,右边的元素都大于基准元素。

总的来说,这两段程序的思路是相同的,都是通过不断地将数组分割为更小的子数组并排序,最后再将子数组合并成排序后的结果。它们的实现细节略有不同,但最终的结果是相同的。
*/

//---------------------------------1------------------------------------
function quickSort(arr) {
    
    
  // 如果数组长度小于等于1,则直接返回
  if (arr.length <= 1) {
    
    
    return arr;
  }

  // 选择一个基准元素
  const pivot = arr[Math.floor(arr.length / 2)];

  // 定义左右两个子数组
  const left = [];
  const right = [];

  // 将元素分割到左右两个子数组
  for (let i = 0; i < arr.length; i++) {
    
    
    if (i === Math.floor(arr.length / 2)) {
    
    
      continue; // 跳过基准元素
    }
    if (arr[i] <= pivot) {
    
    
      left.push(arr[i]);
    } else {
    
    
      right.push(arr[i]);
    }
  }

  // 递归地对左右两个子数组进行快速排序
  return quickSort(left).concat([pivot], quickSort(right));
}

// 测试
const arr = [5, 3, 8, 4, 2, 1, 10];
const sortedArr = quickSort(arr);
console.log(sortedArr); // 输出 [1, 2, 3, 4, 5, 8, 10]


//---------------------------------2------------------------------------
// 快速排序函数
function quickSort1(arr, left, right) {
    
    
  // 递归结束条件
  if (left >= right) {
    
    
    return;
  }

  // 设置左右指针及基准值
  let pivot = arr[left];
  let i = left;
  let j = right;

  // 开始一轮排序
  while (i < j) {
    
    
    // 从右侧找到比基准值小的元素
    while (i < j && arr[j] >= pivot) {
    
    
      j--;
    }

    // 将该元素放到左侧
    arr[i] = arr[j];

    // 从左侧找到比基准值大的元素
    while (i < j && arr[i] <= pivot) {
    
    
      i++;
    }

    // 将该元素放到右侧
    arr[j] = arr[i];
  }

  // 将基准值放回数组
  arr[i] = pivot;

  // 递归调用快速排序函数对左右两个子数组进行排序
  quickSort1(arr, left, i - 1);
  quickSort1(arr, i + 1, right);
}

// 测试
let arr2 = [5, 8, 2, 6, 3, 9, 1, 7, 4];
quickSort1(arr2, 0, arr2.length - 1);
console.log(arr2);

추천

출처blog.csdn.net/jieyucx/article/details/133136490