亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入單鏈表的快速排序詳解

 更新時(shí)間:2013年05月24日 12:57:12   作者:  
本篇文章是對(duì)單鏈表的快速排序進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
單鏈表的快排序和數(shù)組的快排序基本思想相同,同樣是基于劃分,但是又有很大的不同:單鏈表不支持基于下標(biāo)的訪問。故書中把待排序的鏈表拆分為2個(gè)子鏈表。為了簡(jiǎn)單起見,選擇鏈表的第一個(gè)節(jié)點(diǎn)作為基準(zhǔn),然后進(jìn)行比較,比基準(zhǔn)小得節(jié)點(diǎn)放入左面的子鏈表,比基準(zhǔn)大的放入右邊的子鏈表。在對(duì)待排序鏈表掃描一遍之后,左邊子鏈表的節(jié)點(diǎn)值都小于基準(zhǔn)的值,右邊子鏈表的值都大于基準(zhǔn)的值,然后把基準(zhǔn)插入到鏈表中,并作為連接兩個(gè)子鏈表的橋梁。然后分別對(duì)左、右兩個(gè)子鏈表進(jìn)行遞歸快速排序,以提高性能。
但是,由于單鏈表不能像數(shù)組那樣隨機(jī)存儲(chǔ),和數(shù)組的快排序相比較,還是有一些需要注意的細(xì)節(jié):

1、支點(diǎn)的選取,由于不能隨機(jī)訪問第K個(gè)元素,因此每次選擇支點(diǎn)時(shí)可以取待排序那部分鏈表的頭指針。
2、遍歷量表方式,由于不能從單鏈表的末尾向前遍歷,因此使用兩個(gè)指針分別向前向后遍歷的策略實(shí)效,
事實(shí)上,可以可以采用一趟遍歷的方式將較小的元素放到單鏈表的左邊。
具體方法為:
1)定義兩個(gè)指針pslow,pfast,其中pslow指向單鏈表的頭結(jié)點(diǎn),pfast指向單鏈表頭結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn);
2)使用pfast遍歷單鏈表,每遇到一個(gè)比支點(diǎn)小的元素,就令pslow=pslow->next,然后和pslow進(jìn)行數(shù)據(jù)交換。
3、交換數(shù)據(jù)方式,直接交換鏈表數(shù)據(jù)指針指向的部分,不必交換鏈表節(jié)點(diǎn)本身。
基于上述思想的單鏈表快速排序?qū)崿F(xiàn)如下:
復(fù)制代碼 代碼如下:

/**  
** 單鏈表的快速排序  
** author :liuzhiwei
** date   :2011-08-07  
**/
#include<iostream>
#include<ctime>
using namespace std;
//單鏈表節(jié)點(diǎn)
struct SList
{
&nbsp;&nbsp; &nbsp;int data;
&nbsp;&nbsp; &nbsp;struct SList* next;
};
void bulid_slist(SList** phead, int n)&nbsp;&nbsp;&nbsp; //指向指針的指針
{
&nbsp;&nbsp; &nbsp;int i;
&nbsp;&nbsp; &nbsp;SList* ptr = *phead;
&nbsp;&nbsp; &nbsp;for(i = 0; i < n; ++i)
&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;SList* temp = new SList;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;temp->data = rand() % n;&nbsp;&nbsp; //產(chǎn)生n個(gè)n以內(nèi)的隨機(jī)數(shù)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;temp->next = NULL;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if(ptr == NULL)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;*phead = temp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptr = temp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;else
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptr->next = temp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptr = ptr->next;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;}
}
void print_slist(SList* phead)&nbsp;&nbsp; //輸出鏈表
{
&nbsp;&nbsp; &nbsp;SList *ptr = phead;
&nbsp;&nbsp; &nbsp;while(ptr)
&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;printf("%d ", ptr->data);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptr = ptr->next;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;printf("\n");
}
void my_swap(int *a,int *b)
{
&nbsp;&nbsp; &nbsp;int temp;
&nbsp;&nbsp; &nbsp;temp=*a;
&nbsp;&nbsp; &nbsp;*a=*b;
&nbsp;&nbsp; &nbsp;*b=temp;
}
void sort_slist(SList* phead, SList* pend)&nbsp;&nbsp;&nbsp; //將頭指針為phead,尾指針為pend的鏈表進(jìn)行排序
{
&nbsp;&nbsp; &nbsp;if(phead == NULL)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ;
&nbsp;&nbsp; &nbsp;if(phead == pend)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ;
&nbsp;&nbsp; &nbsp;SList *pslow = phead;
&nbsp;&nbsp; &nbsp;SList *pfast = phead->next;
&nbsp;&nbsp; &nbsp;SList *ptemp = phead;
&nbsp;&nbsp; &nbsp;while(pfast != pend)
&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if(pfast->data < phead->data)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每次都選擇待排序鏈表的頭結(jié)點(diǎn)作為劃分的基準(zhǔn)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptemp = pslow;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ptemp始終為pslow的前驅(qū)結(jié)點(diǎn)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;pslow = pslow->next;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;my_swap(&pslow->data , &pfast->data);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //pslow指針指向比基準(zhǔn)小的結(jié)點(diǎn)組成的鏈表
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;pfast = pfast->next;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;my_swap(&pslow->data , &phead->data);&nbsp; //此時(shí)pslow指針指向比基準(zhǔn)小的結(jié)點(diǎn)組成的鏈表的最后一個(gè)結(jié)點(diǎn),也就是基準(zhǔn)的位置,所以要與基準(zhǔn)(head結(jié)點(diǎn))交換
&nbsp;&nbsp; &nbsp;sort_slist(phead , pslow);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ptemp為左右兩部分分割點(diǎn)(基準(zhǔn))的前一個(gè)結(jié)點(diǎn)
&nbsp;&nbsp; &nbsp;sort_slist(pslow->next , NULL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //右部分是比基準(zhǔn)大的結(jié)點(diǎn)組成的鏈表
}
void destroy_slist(SList* phead)
{
&nbsp;&nbsp; &nbsp;SList* ptr = phead;
&nbsp;&nbsp; &nbsp;while(ptr)
&nbsp;&nbsp; &nbsp;{
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;SList* temp = ptr;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptr = ptr->next;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;delete temp;
&nbsp;&nbsp; &nbsp;}
}
int main(void)
{
&nbsp;&nbsp; &nbsp;srand(time(NULL));
&nbsp;&nbsp; &nbsp;printf("Before sort single list\n");
&nbsp;&nbsp; &nbsp;SList* phead = NULL;
&nbsp;&nbsp; &nbsp;bulid_slist(&phead, 100);
&nbsp;&nbsp; &nbsp;print_slist(phead);
&nbsp;&nbsp; &nbsp;printf("After sort single list\n");
&nbsp;&nbsp; &nbsp;sort_slist(phead, NULL);
&nbsp;&nbsp; &nbsp;print_slist(phead);
&nbsp;&nbsp; &nbsp;destroy_slist(phead);
&nbsp;&nbsp; &nbsp;system("pause");
&nbsp;&nbsp; &nbsp;return 0;
}

第二種方法:
選擇鏈表的第一個(gè)節(jié)點(diǎn)作為基準(zhǔn),然后進(jìn)行比較,比基準(zhǔn)小得節(jié)點(diǎn)放入左面的子鏈表,比基準(zhǔn)大的放入右邊的子鏈表。在對(duì)待排序鏈表掃描一遍之后,左面子鏈表的節(jié)點(diǎn)值都小于基準(zhǔn)的值,右邊子鏈表的值都大于基準(zhǔn)的值,然后把基準(zhǔn)插入到鏈表中,并作為連接兩個(gè)子鏈表的橋梁。然后根據(jù)左、右子鏈表中節(jié)點(diǎn)數(shù),選擇較小的進(jìn)行遞歸快速排序,而對(duì)數(shù)目較多的則進(jìn)行迭代排序。
排序函數(shù)中使用的變量如下:
復(fù)制代碼 代碼如下:

 struct node *right;   //右邊子鏈表的第一個(gè)節(jié)點(diǎn)
struct node **left_walk, **right_walk;    //作為指針,把其指向的節(jié)點(diǎn)加入到相應(yīng)的子鏈表中
struct node *pivot, *old;    //pivot為基準(zhǔn), old為循環(huán)整個(gè)待排序鏈表的指針
核心代碼如下:
for (old = (*head)->next; old != end; old = old->next) {
      if (old->data < pivot->data) {  //小于基準(zhǔn),加入到左面的子鏈表,繼續(xù)比較
             ++left_count;
         *left_walk = old;            //把該節(jié)點(diǎn)加入到左邊的鏈表中,
         left_walk = &(old->next);
} else {                      //大于基準(zhǔn),加入到右邊的子鏈表,繼續(xù)比較
         ++right_count;
             *right_walk = old;         
             right_walk = &(old->next);
      }
}

 head為struct node **類型,指向鏈表頭部,end指向鏈表尾部,可為NULL,這段程序的重點(diǎn)在于指針的指針的用法,*left_walk為一個(gè)指向node節(jié)點(diǎn)的指針,說的明白點(diǎn)*left_walk的值就是node節(jié)點(diǎn)的內(nèi)存地址,其實(shí)還有一個(gè)地方也有node的地址,那就是指向node的節(jié)點(diǎn)的next域,故我們可以簡(jiǎn)單的認(rèn)為*left_walk = old就是把指向node節(jié)點(diǎn)的節(jié)點(diǎn)的next域改為節(jié)點(diǎn)old的地址,這樣可能造成兩種情況:一種就是*left_walk本來就指向old節(jié)點(diǎn),這樣就沒有改變?nèi)魏胃淖?,另一種則是改變了*right_walk指向節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)的next域,使其指向后部的節(jié)點(diǎn),中間跳過了若干個(gè)節(jié)點(diǎn),不過在這里這樣做并不會(huì)造成任何問題,因?yàn)殒湵碇械墓?jié)點(diǎn)要么加入到左面的子鏈表中,要么加入到右面的子鏈表中,不會(huì)出現(xiàn)節(jié)點(diǎn)丟失的情況。
下面用圖示說明下上面的問題:


這里假設(shè)鏈表的值一次是5、2、4、6、1。根據(jù)程序首先head = left_walk指向值為5的節(jié)點(diǎn),old指向值為2的節(jié)點(diǎn),2小于5,所以加入2到左面的子鏈表中,*left_walk=old,我們知道,*left_walk指向的是第一個(gè)節(jié)點(diǎn),這樣做改變了head指針值,使其指向第二個(gè)節(jié)點(diǎn),然后left_walk后移,old后移,4同樣小于5,故繼續(xù)上述操作,但是這是*left_walk和old指向的是同一個(gè)節(jié)點(diǎn),沒有引起任何變化,left_walk和old后移,6大于5,這時(shí)不同就出現(xiàn)了,要把其加入到右邊的子鏈表中,故是*right_walk = old,其實(shí)right_walk初試化為&right,這句話相當(dāng)于right = old,即令old當(dāng)前指向的節(jié)點(diǎn)作為右邊子鏈表的第一個(gè)節(jié)點(diǎn),以后大于基準(zhǔn)的節(jié)點(diǎn)都要加入到這個(gè)節(jié)點(diǎn)中,且總是加入到尾部。此時(shí)right_walk,和old后移,1小于5應(yīng)該加入到左邊的子鏈表中,*left_walk = old,此時(shí)*left_walk指向6,故此語句的作用是更改節(jié)點(diǎn)4的next值,把其改為1的地址,這樣6就從原來的鏈表中脫鉤了,繼續(xù)left_walk和old后移到9節(jié)點(diǎn),應(yīng)加入到右邊的子鏈表中,此時(shí)*right_walk指向1,故把9節(jié)點(diǎn)加入到6節(jié)點(diǎn)的后面。

這就是基本的排序過程,然而有一個(gè)問題需要搞明白,比如有節(jié)點(diǎn)依次為struct node *a, *b, *c,node **p , p = &b,如果此時(shí)令*p = c,即實(shí)際效果是a->next = c;我們知道這相當(dāng)于該a的next域的值。而p僅僅是一個(gè)指針的指針,它是指向b所指向的節(jié)點(diǎn)的地址的指針,那么當(dāng)我們更改*p的值的時(shí)候怎么會(huì)改到了a的next呢(這個(gè)可以寫程序驗(yàn)證下,確實(shí)如此)?其實(shí)并非如此,我們仔細(xì)的看看程序,left_walk初始化為head,那么第一次執(zhí)行*left_walk是把head指向了左邊鏈表的起始節(jié)點(diǎn),然后left_walk被賦值為&(old->next),這句話就有意思了,我們看一看下面在執(zhí)行*left_walk=old時(shí)的情況,可以簡(jiǎn)單的來個(gè)等價(jià)替換,*left_walk = old也就相當(dāng)于*&(old->next) = old,即old->nex = old,不過這里的old可不一定是old->next所指向的節(jié)點(diǎn),應(yīng)為left_walk和right_walk都指向它們的old節(jié)點(diǎn),但是卻是不同的。

算法到這里并沒有完,這只是執(zhí)行了一次劃分,把基準(zhǔn)放入了正確的位置,還要繼續(xù),不過下面的就比較簡(jiǎn)單了,就是遞歸排序個(gè)數(shù)比較小的子鏈表,迭代處理節(jié)點(diǎn)數(shù)目比較大的子鏈表。
完整的代碼如下:

復(fù)制代碼 代碼如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <time.h>  
//鏈表節(jié)點(diǎn)  
struct node
{
 int data;  
 struct node *next;  
};  
//鏈表快排序函數(shù)
void QListSort(struct node **head,struct node *h);  
//打印鏈表  
void print_list(struct node *head)
{
 struct node *p;  
 for (p = head; p != NULL; p = p->next)
 {  
  printf("%d ", p->data);  
 }  
 printf("\n");  
}  
int main(void)
{
 struct node *head = NULL;  
 struct node *p;  
 int i;  
 /** 
 * 初始化鏈表 
 */
 srand((unsigned)time(NULL));  
 for (i = 1; i < 11; ++i)
 {  
  p = (struct node*)malloc(sizeof(struct node));  
  p->data = rand() % 100 + 1;
  if(head == NULL)
  {
   head = p;
   head->next = NULL;
  }
  else
  {
   p->next = head->next;
   head->next = p;
  }
 }
 print_list(head);
 printf("---------------------------------\n");
 QListSort(&head,NULL);
 print_list(head);
 system("pause");
 return 0;  
}  
void QListSort(struct node **head, struct node *end)
{
 struct node *right;  
 struct node **left_walk, **right_walk;  
 struct node *pivot, *old;  
 int count, left_count, right_count;  
 if (*head == end)
  return;  
 do {  
  pivot = *head;  
  left_walk = head;
  right_walk = &right;  
  left_count = right_count = 0;  
  //取第一個(gè)節(jié)點(diǎn)作為比較的基準(zhǔn),小于基準(zhǔn)的在左面的子鏈表中,  
  //大于基準(zhǔn)的在右邊的子鏈表中  
  for (old = (*head)->next; old != end; old = old->next)
  {  
   if (old->data < pivot->data)
   {      //小于基準(zhǔn),加入到左面的子鏈表,繼續(xù)比較  
    ++left_count;  
    *left_walk = old;            //把該節(jié)點(diǎn)加入到左邊的鏈表中,  
    left_walk = &(old->next);  
   }
   else
   {                         //大于基準(zhǔn),加入到右邊的子鏈表,繼續(xù)比較  
    ++right_count;  
    *right_walk = old;             
    right_walk = &(old->next);  
   }  
  }  
  //合并鏈表  
  *right_walk = end;       //結(jié)束右鏈表  
  *left_walk = pivot;      //把基準(zhǔn)置于正確的位置上  
  pivot->next = right;     //把鏈表合并  
  //對(duì)較小的子鏈表進(jìn)行快排序,較大的子鏈表進(jìn)行迭代排序。  
  if(left_walk > right_walk)
  {
   QListSort(&(pivot->next), end);  
   end = pivot;  
   count = left_count;  
  }
  else
  {  
   QListSort(head, pivot);  
   head = &(pivot->next);  
   count = right_count;  
  }  
 }
 while (count > 1);   
}

相關(guān)文章

  • C++超集C++/CLI模塊的基本語法

    C++超集C++/CLI模塊的基本語法

    這篇文章介紹了C++超集C++/CLI模塊的基本語法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 用C語言實(shí)現(xiàn)通訊錄

    用C語言實(shí)現(xiàn)通訊錄

    這篇文章主要為大家詳細(xì)介紹了用C語言實(shí)現(xiàn)通訊錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C++ STL array容器訪問元素的幾種方式

    C++ STL array容器訪問元素的幾種方式

    這篇文章主要介紹了C++ STL array容器訪問元素的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Qt中關(guān)聯(lián)容器QMap,QMultiMap,QHash,QMultiHash的使用

    Qt中關(guān)聯(lián)容器QMap,QMultiMap,QHash,QMultiHash的使用

    本文主要介紹了Qt中關(guān)聯(lián)容器QMap,QMultiMap,QHash,QMultiHash的使用,這些關(guān)聯(lián)容器在Qt中提供了靈活而強(qiáng)大的數(shù)據(jù)結(jié)構(gòu)選項(xiàng),根據(jù)具體的需求和使用場(chǎng)景,您可以選擇適合的容器來存儲(chǔ)和管理數(shù)據(jù),感興趣的可以了解一下
    2023-09-09
  • C語言實(shí)現(xiàn)圖書館管理系統(tǒng)

    C語言實(shí)現(xiàn)圖書館管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)圖書館管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Qt實(shí)現(xiàn)邊加載數(shù)據(jù)邊顯示頁面的示例代碼

    Qt實(shí)現(xiàn)邊加載數(shù)據(jù)邊顯示頁面的示例代碼

    無論是MFC框架還是QT框架,實(shí)現(xiàn)加載數(shù)據(jù)的等待效果都是很麻煩的,不像WEB端輕輕松松一句代碼就搞定了。本文將通過Qt實(shí)現(xiàn)邊加載數(shù)據(jù)邊顯示頁面的功能,需要的可以參考一下
    2022-01-01
  • C++ 如何判斷四個(gè)點(diǎn)是否構(gòu)成正方形

    C++ 如何判斷四個(gè)點(diǎn)是否構(gòu)成正方形

    這篇文章主要介紹了C++ 如何判斷四個(gè)點(diǎn)是否構(gòu)成正方形的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • c++ Protobuf解決數(shù)據(jù)傳輸瓶頸面試精講

    c++ Protobuf解決數(shù)據(jù)傳輸瓶頸面試精講

    這篇文章主要介紹了c++ Protobuf解決數(shù)據(jù)傳輸瓶頸利器面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 詳解C++中typedef 和 #define 的區(qū)別

    詳解C++中typedef 和 #define 的區(qū)別

    這篇文章主要介紹了C++中typedef 與 #define 的區(qū)別,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • C語言實(shí)現(xiàn)讀取CSV文件的方法詳解

    C語言實(shí)現(xiàn)讀取CSV文件的方法詳解

    這篇文章主要為大家詳細(xì)介紹了C語言如何實(shí)現(xiàn)讀取CSV文件,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2022-12-12

最新評(píng)論