代码,非剑指offer 2

1.堆排序

// 根据数组构建大堆  
void HeapAdjust( int* arr, int i, int len ){  
   int child;  
   int tmp;  
 
   for (; 2 * i + 1 < len; i = child){  
       // 子结点 = 2 * 父结点 + 1  
       child = 2 * i + 1;  
 
       // 得到子结点中数值较大的结点  
       if (child < len-1 && arr[child+1] > arr[child])  
           ++child;  
 
       // 如果较大的子结点值大于父结点的值那么把较大的子结点往上移动,替换它的父结点  
       if (arr[i] < arr[child]){  
           tmp = arr[i];  
           arr[i] = arr[child];  
           arr[child] = tmp;   
       }  
       else   
           break;   
   }  
}  
 
//堆排序算法  
void HeapSort( int* arr, int len ){  
   int i;  
 
   // length/2-1是最后一个非叶节点  
   // 构建大堆  
   for (i = len/2-1; i >= 0; --i)  
       HeapAdjust( arr, i, len );  
 
   // 从最后一个元素开始对序列进行调整,不断缩小调整范围直到第一个元素  
   for (i = len-1; i > 0; --i){  
       // 把第一个元素和当前的最后一个元素交换  
       // 保证当前最后一个位置的元素都是现在这个序列之中最大的元素  
       arr[i] = arr[0] ^ arr[i];  
       arr[0] = arr[0] ^ arr[i];  
       arr[i] = arr[0] ^ arr[i];  
 
       // 不断缩小调整范围,每一次调整完毕后保证第一个元素是当前序列的最大值  
       HeapAdjust( arr, 0, i );  
   }  
}
2.

希尔排序(插入排序的改进版本),希尔排序的步长现在还没有最好的选择,不过已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,...)

操作步骤:

初始时,有一个大小为 10 的无序序列。
(1)在第一趟排序中,我们不妨设 gap1 = N / 2 = 5,即相隔距离为 5 的元素组成一组,可以分为 5 组。
(2)接下来,按照直接插入排序的方法对每个组进行排序。
在第二趟排序中,我们把上次的 gap 缩小一半,即 gap2 = gap1 / 2 = 2 (取整数)。这样每相隔距离为 2 的元素组成一组,可以分为 2 组。
(3)按照直接插入排序的方法对每个组进行排序。
(4)在第三趟排序中,再次把 gap 缩小一半,即gap3 = gap2 / 2 = 1。 这样相隔距离为 1 的元素组成一组,即只有一组。
(5)按照直接插入排序的方法对每个组进行排序。此时,排序已经结束。

时间复杂度:


最好情况:由于希尔排序的好坏和步长gap的选择有很多关系,因此,目前还没有得出最好的步长如何选择(现在有些比较好的选择了,但不确定是否是最好的)。所以,不知道最好的情况下的算法时间复杂度。
最坏情况下:O(N*N)


空间复杂度:

由直接插入排序算法可知,我们在排序过程中,需要一个临时变量存储要插入的值,所以空间复杂度为O(1)。


算法稳定性

希尔排序中相等数据可能会交换位置,所以希尔排序是不稳定的算法。
 在使用 增量k的一趟排序后,对于每一个i,我们有 a[i] <= a[i + k]。所有相隔k的元素都被排序。希尔排序一个重要性质:一个k排序的文件保持它的k排序性。如果情况不是这样,该排序就没有意义了,因为后面各趟排序会打乱前面各趟排序的成果。

k排序的一般做法:对于i,i+k,i+2k...中的每一个位置,都把其上的元素放到前面序列中的正确位置上。

一趟k排序作用是对k个独立的子数组进行一次插入排序。

template <typename T>  
void ShellSort( vector<T>& a ){  
   for (int gap = a.size( ) / 2; gap > 0; gap /= 2)  
       for (int i = gap; i < a.size( ); ++i){  
           T tmp = a[i];  
           int j = i;  
 
           for (; j >= gap && tmp < a[j - gap]; j-= gap)  
               a[j] = a[j - gap];  
 
           a[j] = tmp;  
       }  
}


3.

归并排序

合并两个已排序的表时间是线性的,最多进行N-1次比较,N是两个表元素的总数,因为每次比较都把一个元素添加到临时数组中,但最后一次比较除外,它至少添加两个元素。

如果对Merge的每个递归调用均局部声明一个临时数组,那么在任一时刻就可能有 logN个临时数组处在活动期。   由于Merge是MergeSort的最后一行,因此在任一时刻只需要一个临时数组活动,而且这个临时数组可以在MergeSort驱动程序中建立。还可以使用临时数组的任意部分。我们将使用与输入数组a相同的部分

template <typename T>  
void MergeSort( vector<T>& a ){  
   vector<T> tmpArr( a.size( ) );  
 
   MergeSort( a, tmpArr, 0, a.size( ) - 1 );  
}  
 
template <typename T>  
void MergeSort( vector<T>& a, vector<T>& tmpArr, int left, int right ){  
   if (left < right){  
       int center = ( left + right ) / 2;  
       MergeSort( a, tmpArr, left, center );  
       MergeSort( a, tmpArr, left+1, right );  
       Merge( a, tmpArr, left, center+1, right );  
   }  
}  
 
template <typename T>  
void Merge( vector<T>& a, vector<T>& tmpArr, int leftPos, int rightPos, int rightEnd ){  
   int leftEnd = rightPos - 1;  
   int tmpPos = leftPos;  
   int num = rightPos - leftPos + 1;  
 
   while (leftPos <= leftEnd && rightPos <= rightEnd)  
       if (a[leftPos] <= a[rightPos])  
           tmpArr[tmpPos++] = a[leftPos++];  
       else  
           tmpArr[tmpPos++] = a[rightPos++];  
 
   while (leftPos <= leftEnd)  
       tmpArr[tmpPos++] = a[leftPos++];  
 
   while (rightPos <= rightEnd)  
       tmpArr[tmpPos++] = a[rightEnd++];  
 
   for (int i = 0; i < num; i++, rightEnd--)  
       a[rightEnd] = tmpArr[rightEnd];  
}
4.

实现时间复杂度为O(nlogn)的链表排序算法

链接:点击打开链接

链表排序是一道常见的关于链表的算法题,往往我们会使用冒泡排序或者选择排序这两种算法来解决这个问题。但是它们的时间复杂度是O(n²),效率不高。所以今天我们要实现时间复杂度O(nlogn)的算法就不能再选择这两种排序算法。而满足这个时间复杂度的排序算法只有快速排序,堆排序和归并排序。但是因为快排和堆排是不稳定的,所以我选择了归并排序算法来实现链表排序问题。

下面给出代码实现


class ListNode 
{//结构定义
public:
    int val;
    ListNode *next;
    ListNode(int val) 
    {
        this->val = val;
        this->next = NULL;
    }   
};

ListNode* SortList(ListNode* head)
{
    if (head == NULL || head->next == NULL)
        return head;

    ListNode* fast;
    ListNode* slow;

    while (fast->next != NULL && fast->next->next != NULL)
    {//通过快慢指针法寻找中间结点
        fast = fast->next;
        fast = fast->next;
        slow = slow->next;
    }

    ListNode* mid = slow->next;
    slow->next == NULL;//把链表拆分开为两个链表

    //分治思想,递归
    ListNode* list1 = SortList(head);
    ListNode* list2 = SortList(mid);

    ListNode* sorted = Merge(list1, list2);
    return sorted;
}

ListNode* Merge(ListNode* list1, ListNode* list2)
{//合并
    if (list1 == NULL)
        return list2;
    if (list2 == NULL)
        return list1;

    ListNode* head;
    ListNode* tmp;

    if (list1->val < list2->val)
    {
        head = list1;
        list1 = list1->next;
    }
    else
    {
        head = list2;
        list2 = list2->next;
    }

    tmp = head;

    while (list1 && list2)
    {
        if (list1->val < list2->val)
        {
            tmp->next = list1;
            tmp = tmp->next;
            list1 = list1->next;
        }
        else
        {
            tmp->next = list2;
            tmp = tmp->next;
            list2 = list2->next;
        }
    }

    if (list1 == NULL)
    {
        tmp->next = list2;
    }
    if (list2 == NULL)
    {
        tmp->next = list1;
    }

    return head;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值