《图解算法》学习笔记之快速排序

目录

1 欧几里得算法

1.1 D&C的工作原理:

1.2 函数式编程一瞥

2 快速排序

3 归纳证明

4 快速排序伪代码

5 再谈大 O 表示法

5.0 《图解算法》学习之算法复杂度、运行时间

5.1 比较合并排序和快速排序

5.2 平均情况和最糟情况

6 小结

7 练习

 7.1 请编写前述sum函数的代码。

7.1.1 Python 循环求数组之和

7.1.2 Python 递归求和

7.1.3 JS

7.1.4 C 循环

7.1.5 C递归

7.1.6 C ++ 11 loop sum

7.1.7  C++11 递归

7.1.8 C# loop sum

7.1.9 C# recursive_sum

7.1.10 Java loop sum

7.1.11 Java recursiveSUm

7.2 编写一个递归函数来计算列表包含的元素数。

 7.2.1 Python

7.2.2 JS

7.2.3 C++11

7.2.4 C#

7.2.5 Java

7.3 找出列表中最大的数字。

7.3.1 Python

7.3.2 JS

7.3.3 C++11

7.3.4 C#

7.3.5 java

7.4 快速排序

7.4.1 Python

7.4.2 JS

7.4.3 C

7.4.4 C++11 

7.4.5 C#

7.4.6 Java



我们将探索分而治之(divide and conquer, D&C) ——一种著名的递归式问题解决方法。
 

1 欧几里得算法
 

https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/the-euclidean-algorithm

可汗学院算法 https://www.khanacademy.org/computing/computer-science/algorithms

1.1 D&C的工作原理:


(1) 找出简单的基线条件;
(2) 确定如何缩小问题的规模,使其符合基线条件。
D&C并非可用于解决问题的算法,而是一种解决问题的思路。

你需要将这些数字相加,并返回结果。使用循环很容易完成这种任务。
 

1.2 函数式编程一瞥

你可能想,既然使用循环可轻松地完成任务,为何还要使用递归方式呢?看看函数式编程
你就明白了!诸如Haskell等函数式编程语言没有循环,因此你只能使用递归来编写这样的函数。
如果你对递归有深入的认识,函数式编程语言学习起来将更容易。例如,使用Haskell时,你可
能这样编写函数sum。
sum [] = 0
sum (x:xs) = x + (sum xs)
注意,这就像是你有函数的两个定义。符合基线条件时运行第一个定义,符合递归条件时
运行第二个定义。也可以使用Haskell语言中的if语句来编写这个函数。
sum arr = if arr == []
then 0
else (head arr) + (sum (tail arr))
但前一个版本更容易理解。 Haskell大量使用了递归,因此它提供了各种方便实现递归的语
法。如果你喜欢递归或想学习一门新语言,可以研究一下Haskell。

2 快速排序

快速排序是一种常用的排序算法,比选择排序快得多。例如, C语言标准库中的函数qsort实现的就是快速排序。快速排序也使用了D&C。
 

基线条件为数组为空或只包含一个元素。在这种情况下,只需原样返回数组——根本就不用排序。
 

def quicksort(array):
   if len(array) < 2:
      return array


这被称为分区(partitioning) 。现在你有:
 一个由所有小于基准值的数字组成的子数组;

 基准值;
 一个由所有大于基准值的数组组成的子数组。
这里只是进行了分区,得到的两个子数组是无序的。但如果这两个数组是有序的,对整个数
组进行排序将非常容易
 

3 归纳证明

刚才你大致见识了归纳证明!归纳证明是一种证明算法行之有效的方式,它分两步:基线条件和归纳条件。是不是有点似曾相识的感觉?例如,假设我要证明我能爬到梯子的最上面。递归条件是这样的:如果我站在一个横档上,就能将脚放到下一个横档上。换言之,如果我站在第二个横档上,就能爬到第三个横档。这就是归纳条件。而基线条件是这样的,即我已经站在第一个横档上。因此,通过每次爬一个横档,我就能爬到梯子最顶端。对于快速排序,可使用类似的推理。在基线条件中,我证明这种算法对空数组或包含一个元素的数组管用。在归纳条件中,我证明如果快速排序对包含一个元素的数组管用,对包含两个元素的数组也将管用;如果它对包含两个元素的数组管用,对包含三个元素的数组也将管用,以此类推。因此,我可以说,快速排序对任何长度的数组都管用。这里不再深入讨论归纳证明,但它很有趣,并与D&C协同发挥作用。

4 快速排序伪代码

5 再谈大 O 表示法

5.0 《图解算法》学习之算法复杂度、运行时间

《图解算法》学习之算法复杂度、运行时间_moonlightpeng的博客-CSDN博客

快速排序的独特之处在于,其速度取决于选择的基准值。在讨论快速排序的运行时间前,我们再来看看最常见的大O运行时间。
 

上述图表中的时间是基于每秒执行10次操作计算得到的。这些数据并不准确,这里提供它们只是想让你对这些运行时间的差别有大致认识。实际上,计算机每秒执行的操作远不止10次。

对于每种运行时间,本书还列出了相关的算法。来看看第2章介绍的选择排序,其运行时间为O(n2),速度非常慢。
还有一种名为合并排序(merge sort) 的排序算法,其运行时间为O(n log n),比选择排序快得多!快速排序的情况比较棘手,在最糟情况下,其运行时间为O(n2)。
与选择排序一样慢!但这是最糟情况。在平均情况下,快速排序的运行时间为O(n log n)。你可能会有如下疑问。


 这里说的最糟情况和平均情况是什么意思呢?
 若快速排序在平均情况下的运行时间为O(n log n),而合并排序的运行时间总是O(n log n),为何不使用合并排序?它不是更快吗?

 

5.1 比较合并排序和快速排序

但有时候,常量的影响可能很大,对快速查找和合并查找来说就是如此。快速查找的常量比
合并查找小,因此如果它们的运行时间都为O(n log n),快速查找的速度将更快。实际上,快速查
找的速度确实更快,因为相对于遇上最糟情况,它遇上平均情况的可能性要大得多。


此时你可能会问,何为平均情况,何为最糟情况呢?
 

5.2 平均情况和最糟情况
 

6 小结


 D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元
素的数组。
 实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为O(n log n)。
 大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在。
 比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时, O(log n)的速度比O(n)
快得多。
 

7 练习

 7.1 请编写前述sum函数的代码。

7.1.1 Python 循环求数组之和

def computSum(arrays):
    addSum = 0
    for num in arrays:
        addSum += num
    return addSum


myArray = [1,2,3,4,5]
print(computSum(myArray))

7.1.2 Python 递归求和

def max_(lst):
  if len(lst) == 0:
    return None
  if len(lst) == 1:
    return lst[0]
  else:
    sub_max = max_(lst[1:])
    return lst[0] if lst[0] > sub_max else sub_max


myArray = [1,20,3,4]
print(max_(myArray))

7.1.3 JS

/**
 * Sums values in array by loop "for"
 * @param {Array} arr Array of numbers
 * @return {total} Sum of the numbers
 */

function sum(arr) {
    let total = 0;
    for (let i = 0; i < arr.length; i++) {
        total += arr[i];
    }
    return total;
}

console.log(sum([1, 2, 3, 4])); // 10

/**
 * Sums values in array by function "reduce"
 * @param {Array} arr Array of numbers
 * @return {number} Sum of the numbers
 */
function sumReduce( arr ) {
    var result = newArr.reduce( ( curr, prev ) => {
        return curr + prev;
    } );

    return result;
}

var arr = [1, 2, 3, 4];

console.log( sumReduce( arr ) ); // 10

const arr = [1, 2, 3, 4];

/**
 * Sums values in array recursively
 * @param {Array} arr Array of numbers
 * @return {number} Sum of the numbers
 */
const sumRecursive = ( arr ) => {
    if ( arr.length == 1 ) return arr[0];
    return arr[0] + sumRecursive( arr.slice( 1 ) );
};

console.log( sumRecursive( arr ) );  // 10

7.1.4 C 循环

#include <stdio.h>

int sum(int *arr, int size) {
	int total = 0;
	for (int i = 0; i < size; i++) {
		total += arr[i];
	}
	return total;
}

int main(void) {
	int arr[4] = { 1,2,3,4 };
	printf("%d", sum(arr, 4));

	return 0;
}

7.1.5 C递归

#include <stdio.h>

int sum(int *arr, int index, int size) {
	if (index == size)
		return 0;
	return arr[index] + sum(arr, index + 1, 4);
}

int main(void) {
	int arr[4] = { 1,2,3,4 };
	printf("%d", sum(arr, 0, 4));

	return 0;
}

7.1.6 C ++ 11 loop sum

#include <iostream>
#include <vector>

using std::cout;
using std::endl;

template <typename T>
T sum(const std::vector<T>& arr) {
    T sum = 0;
    for (T item : arr) {
        sum += item;
    }

    return sum;
}

int main() {
    std::vector<int> arr_int = {1, 2, 3, 4};
    std::vector<float> arr_float = {0.1, 0.2, 0.3, 0.4, 0.5};

    cout << "Sum ints: " << sum(arr_int) << endl;
    cout << "Sum floats: " << sum(arr_float) << endl;
}

7.1.7  C++11 递归

#include <iostream>
#include <vector>

using std::cout;
using std::endl;

template <typename T>
T sum(std::vector<T> arr) {
    if (arr.empty()) return 0;

    T last_num = arr.back(); // save last number value
    arr.pop_back();  // and remove it from array for next recursive call
    return first_num + sum(arr);
}

int main() {
    std::vector<int> arr_int = {1, 2, 3, 4};
    std::vector<float> arr_float = {0.1, 0.2, 0.3, 0.4, 0.5};

    cout << "Sum ints: " << sum(arr_int) << endl;
    cout << "Sum floats: " << sum(arr_float) << endl;
}

7.1.8 C# loop sum

using System;
using System.Collections.Generic;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(Sum(new[] { 1, 2, 3, 4 }));
        }

        private static int Sum(IEnumerable<int> arr)
        {
            var total = 0;
            foreach (var x in arr)
            {
                total += x;
            }
            return total;
        }
    }
}

7.1.9 C# recursive_sum

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(Sum(new[] { 1, 2, 3, 4 }));
        }

        private static int Sum(IEnumerable<int> list)
        {
            if (!list.Any()) return 0;

            return list.Take(1).First() + Sum(list.Skip(1));
        }
    }
}

7.1.10 Java loop sum

public class LoopSum {

    private static int sum(int[] arr) {
        int total = 0;
        for (int x = 0; x < arr.length; x++) {
            total += arr[x];
        }

        return total;
    }

    public static void main(String[] args) {
        System.out.println(sum(new int[]{1, 2, 3, 4})); // 10
    }
}

7.1.11 Java recursiveSUm

import java.util.Arrays;

public class RecursiveSum {

    private static int sum(int[] arr) {
        if (arr.length == 0) {
            return 0;
        } else {
            return arr[0] + sum(Arrays.copyOfRange(arr, 1, arr.length));
        }
    }

    public static void main(String[] args) {
        System.out.println(sum(new int[]{1, 2, 3, 4})); // 10
    }
}

7.2 编写一个递归函数来计算列表包含的元素数。

 7.2.1 Python

def count(list):
  if list == []:
    return 0
  return 1 + count(list[1:])

myArray = [1,2,3,4]
print(count(myArray))

7.2.2 JS

'use strict';

function count(list) {
  if (list.length === 0) {
    return 0;
  }
  return 1 + count(list.slice(1));
}

console.log(count([0, 1, 2, 3, 4, 5])); // 6

7.2.3 C++11

#include <iostream>
#include <vector>

using std::cout;
using std::endl;

template <typename T>
int count(std::vector<T> arr) {
    if (arr.empty()) return 0;
    arr.pop_back();
    return count(arr) + 1;
}

int main() {
    std::vector<int> array = {0, 1, 2, 3, 4, 5};
    cout << count(array) << endl;
}

7.2.4 C#

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(Count(new[] { 1, 2, 3, 4 }));
        }

        private static int Count(IEnumerable<int> list)
        {
            if(!list.Any()) return 0;

            return 1 + Count(list.Skip(1));
        }
    }
}

7.2.5 Java

import java.util.Arrays;

public class RecursiveCount {

    private static int count(int[] list) {
        if (list.length == 0) {
            return 0;
        }

        return 1 + count(Arrays.copyOfRange(list, 1, list.length));
    }

    public static void main(String[] args) {
        System.out.println(count(new int[]{0, 1, 2, 3, 4, 5})); // 6
    }
}

7.3 找出列表中最大的数字。

7.3.1 Python

def recursive_Sum(arrays):
    if arrays == []:
        return 0
    else:
        return arrays[0] + recursive_Sum(arrays[1:])

myArray = [1,2,3,4,5]
num = recursive_Sum(myArray)
print(num)

7.3.2 JS

'use strict';

function max(list) {
  if (list.length === 2) {
    return list[0] > list[1] ? list[0] : list[1];
  }
  let sub_max = max(list.slice(1));
  return list[0] > sub_max ? list[0] : sub_max;
}

console.log(max([1, 5, 10, 25, 16, 1])); // 25

7.3.3 C++11

#include <iostream>
#include <vector>
#include <stdexcept>

using std::cout;
using std::endl;

template <typename T>
T max(std::vector<T> arr) {
    if (arr.empty()) throw std::invalid_argument("Cannot select max value from empty sequence");
    if (arr.size() == 1) return arr.at(0);

    T back = arr.back();
    arr.pop_back();

    T sub_max = max(arr);
    
    return back > sub_max ? back : sub_max;
}

int main() {
    std::vector<int> array = {1, 5, 10, 25, 16, 1};
    cout << max(array) << endl;

    std::vector<int> negative_array = {-1, -5, -10, -25, -16};
    cout << max(negative_array) << endl;
}

7.3.4 C#

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(Max(new[] { 1, 3, 2, 5, 9, 8 }));
        }

        private static int Max(IEnumerable<int> list)
        {
            if (!list.Any()) throw new ArgumentException(nameof(list));
            if (list.Count() == 1) return list.First();
            if (list.Count() == 2) return list.First() > list.Skip(1).Take(1).First() ? list.First() : list.Skip(1).Take(1).First();
            var sub_max = Max(list.Skip(1));
            return list.First() > sub_max ? list.First() : sub_max;
        }
    }
}

7.3.5 java

import java.util.Arrays;

public class RecursiveMax {

    private static int max(int[] list) {
        if (list.length == 2) {
            return list[0] > list[1] ? list[0] : list[1];
        }

        int subMax = max(Arrays.copyOfRange(list, 1, list.length));
        return list[0] > subMax ? list[0] : subMax;
    }

    public static void main(String[] args) {
        System.out.println(max(new int[]{1, 5, 10, 25, 16, 1})); // 25
    }
}

7.4 快速排序

7.4.1 Python

def quicksort(array):
  if len(array) < 2:
    # base case, arrays with 0 or 1 element are already "sorted"
    return array
  else:
    # recursive case
    pivot = array[0]
    # sub-array of all the elements less than the pivot
    less = [i for i in array[1:] if i <= pivot]
    # sub-array of all the elements greater than the pivot
    greater = [i for i in array[1:] if i > pivot]
    return quicksort(less) + [pivot] + quicksort(greater)

print(quicksort([10, 5, 2, 3]))

myArray = [1,20,300,4]
print(quicksort(myArray))

7.4.2 JS

'use strict';

function quicksort(array) {
  if (array.length < 2) {
    // base case, arrays with 0 or 1 element are already "sorted"
    return array;
  } else {
   // recursive case
   let pivot = array[0];
   // sub-array of all the elements less than the pivot
   let less = array.slice(1).filter(function(el) { return el <= pivot; });
   // sub-array of all the elements greater than the pivot
   let greater = array.slice(1).filter(function(el) { return el > pivot; });
   return quicksort(less).concat([pivot], quicksort(greater));
  }
}

console.log(quicksort([10, 5, 2, 3])); // [2, 3, 5, 10]

7.4.3 C

#include <stdio.h>

// Quick Sort
void quick_sort(int *array, int start, int end) {
	if (start < end) {
		int q = partition(array, start, end);
		quick_sort(array, start, q - 1);
		quick_sort(array, q + 1, end);
	}
}
// Partition by pivot
int partition(int *array, int start, int end) {
	int pivot = array[end];
	int i = start - 1;
	int temp = 0;
	for (int j = start; j < end; j++) {
		if (array[j] <= pivot) {
			i++;
			temp = array[i];
			array[i] = array[j];
			array[j] = temp;
		}
	}
	temp = array[i + 1];
	array[i + 1] = array[end];
	array[end] = temp;

	return i + 1;
}

int main(void) {
	int arr[4] = {10, 5, 2, 3};
	quick_sort(arr, 0, 3);
	// Print result
	for (int i = 0; i < 4; i++) {
		printf("%d ", arr[i]);
	}

	return 0;
}

7.4.4 C++11 

#include <iostream>
#include <vector>

using std::cout;
using std::endl;

template <typename T>
std::vector<T> quicksort(const std::vector<T>& arr) {
    // base case, arrays with 0 or 1 element are already "sorted"
    if (arr.size() < 2) 
        return arr;
    
    // recursive case
    const T* pivot = &arr.front() + arr.size() / 2 - 1; // set the pivot somewhere in the middle
    std::vector<T> less;  // vector to store all the elements less than the pivot
    std::vector<T> greater;  // vector to store all the elements greater than the pivot

    for (const T* item = &arr.front(); item <= &arr.back(); item++) {
        if (item == pivot) continue; // skip pivot element
        if (*item <= *pivot) less.push_back(*item);
        else greater.push_back(*item);
    }
    
    std::vector<T> sorted_less = quicksort(less);
    std::vector<T> sorted_greater = quicksort(greater);
    // concatenate less part, pivot and greater part
    sorted_less.push_back(*pivot);
    sorted_less.insert(sorted_less.end(), sorted_greater.begin(), sorted_greater.end());
    
    return sorted_less;
}

int main() {
    std::vector<int> arr = {69, 60, 38, 82, 99, 15, 8, 94, 30, 42, 35, 40, 63, 1, 49, 66, 93, 83, 20, 32, 87, 6, 78, 17, 2, 61, 91, 25, 7, 4, 97, 31, 23, 67, 95, 47, 55, 92, 37, 59, 73, 81, 74, 41, 39};
    std::vector<int> sorted = quicksort(arr);
    for (int num : sorted) {
        cout << num << " ";
    }
    cout << endl;
}

7.4.5 C#

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var arr = new[] { 10, 5, 2, 3 };
            Console.WriteLine(string.Join(", ", QuickSort(arr)));
        }

        private static IEnumerable<int> QuickSort(IEnumerable<int> list)
        {
            if (list.Count() <= 1) return list;
            var pivot = list.First();
            var less = list.Skip(1).Where(i => i <= pivot);
            var greater = list.Skip(1).Where(i => i > pivot);
            return QuickSort(less).Union(new List<int> { pivot }).Union(QuickSort(greater));
        }
    }
}

7.4.6 Java

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Quicksort {
    public static void main(String[] args) {
        System.out.println(quicksort(Arrays.asList(10, 5, 2, 3))); // [2, 3, 5, 10]
    }

    private static List<Integer> quicksort(List<Integer> list) {
        if (list.size() < 2) {
            // base case, arrays with 0 or 1 element are already "sorted"
            return list;
        } else {
            // recursive case
            Integer pivot = list.get(0);

            // sub-array of all the elements less than the pivot
            List<Integer> less = list.stream().skip(1).filter(el -> el <= pivot)
                    .collect(Collectors.toList());

            // sub-array of all the elements greater than the pivot
            List<Integer> greater = list.stream().skip(1).filter(el -> el > pivot)
                    .collect(Collectors.toList());

            return Stream.of(
                    quicksort(less).stream(),
                    Stream.of(pivot),
                    quicksort(greater).stream())
                    .flatMap(Function.identity()).collect(Collectors.toList());
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值