堆排序

来源:互联网 发布:淘宝千色药妆是正品么 编辑:程序博客网 时间:2024/05/17 03:09

一些概念

普通队列:先进先出,后进先出(顺序已经确定)
优先队列:出队顺序与入队顺序无关,和优先级相关(动态确定顺序)

优先队列在处理动态问题上很有优势,同时处理静态问题也是比较好的,比如在100000000个元素中选出前100名?也就是,在N个元素中选出前M个元素。不同方法的时间复杂度分别是排序:NlogN ,优先队列:NlogM

动态

有新的元素会加入数据结构,同时又有元素会离开队列,也就是说入队和出队是有限队列的两个基本特征。

出队:取出优先级最高的元素。

堆的存储

这里写图片描述

1)节点值总是不大于父亲节点的值;
2)完全二叉树。

由于堆是一颗完全二叉树,所以可以用数组来存储一颗二叉树。

这里写图片描述

ShiftUp

入队时,从末端加入元素,然后要调整元素顺序,使其任然维持最大堆的定义,关键操作是每次和父亲相比较,逐层向上升直到满足堆的定义。

void shiftUp(int k){    while (k > 1 && data[k / 2] < data[k]){        swap(data[k / 2], data[k]);        k /= 2;    }}

ShiftDown

出队只能取出根节点的元素,出队后为了保证完全二叉树,可以将最后一个元素与根节点位置交换,然后对根节点元素进行ShiftDown操作。

void shiftDown(int k){    while (2 * k <= count){        int j = 2 * k; // 在此轮循环中,data[k]和data[j]交换位置        if (j + 1 <= count && data[j + 1] > data[j])            j++;        // data[j] 是 data[2*k]和data[2*k+1]中的最大值        if (data[k] >= data[j]) break;        swap(data[k], data[j]);        k = j;    }}

堆的数据结构

template<typename Item>class MaxHeap{private:    Item *data;    int count;    int capacity;    void shiftUp(int k){        while (k > 1 && data[k / 2] < data[k]){            swap(data[k / 2], data[k]);            k /= 2;        }    }    void shiftDown(int k){        while (2 * k <= count){            int j = 2 * k; // 在此轮循环中,data[k]和data[j]交换位置            if (j + 1 <= count && data[j + 1] > data[j])                j++;            // data[j] 是 data[2*k]和data[2*k+1]中的最大值            if (data[k] >= data[j]) break;            swap(data[k], data[j]);            k = j;        }    }public:    MaxHeap(int capacity){        data = new Item[capacity + 1];        count = 0;        this->capacity = capacity;    }    ~MaxHeap(){        delete[] data;    }    int size(){        return count;    }    bool isEmpty(){        return count == 0;    }    void insert(Item item){        assert(count + 1 <= capacity);        data[count + 1] = item;        shiftUp(count + 1);        count++;    }    Item extractMax(){        assert(count > 0);        Item ret = data[1];        swap(data[1], data[count]);        count--;        shiftDown(1);        return ret;    }    Item getMax(){        assert(count > 0);        return data[1];    }};

Heapify

MaxHeap(Item arr[], int n){    data = new Item[n + 1];    capacity = n;    for (int i = 0; i < n; i++)        data[i + 1] = arr[i];    count = n;    for (int i = count / 2; i >= 1; i--)        shiftDown(i);}

堆排序

template<typename T>void heapSort2(T arr[], int n){    MaxHeap<T> maxheap = MaxHeap<T>(arr,n);    for( int i = n-1 ; i >= 0 ; i-- )        arr[i] = maxheap.extractMax();}template<typename T>void heapSort1(T arr[], int n){    MaxHeap<T> maxheap = MaxHeap<T>(n);    for( int i = 0 ; i < n ; i ++ )        maxheap.insert(arr[i]);    for( int i = n-1 ; i >= 0 ; i-- )        arr[i] = maxheap.extractMax();}

使用Heapify构建堆,时间复杂度是O(n),而使用入队操作构建一个堆时间复杂度是O(nlgn)

堆排序优化

原地堆排序,空间复杂度是O(1),值得注意的是由于是在原地,因此堆的索引是从0就开始的,也就是要重新换算下父亲及叶子节点之间的关系。

这里写图片描述

template<typename T>void __shiftDown(T arr[], int n, int k){    while( 2*k+1 < n ){        int j = 2*k+1;        if( j+1 < n && arr[j+1] > arr[j] )            j += 1;        if( arr[k] >= arr[j] )break;        swap( arr[k] , arr[j] );        k = j;    }}template<typename T>void __shiftDown2(T arr[], int n, int k){    T e = arr[k];    while( 2*k+1 < n ){        int j = 2*k+1;        if( j+1 < n && arr[j+1] > arr[j] )            j += 1;        if( e >= arr[j] ) break;        arr[k] = arr[j];        k = j;    }    arr[k] = e;}template<typename T>void heapSort(T arr[], int n){    // 首先进行Heapify操作    for( int i = (n-1)/2 ; i >= 0 ; i -- )        __shiftDown2(arr, n, i);    // 然后每次把堆顶元素与最后一个叶子交换,之后堆堆顶元素进行shiftDown操作    for( int i = n-1; i > 0 ; i-- ){        swap( arr[0] , arr[i] );        __shiftDown2(arr, i, 0);    }}
原创粉丝点击