排序算法

[TOC]

前言

這是《Java數據結構與算法》一書中關于排序算法部分的讀書筆記纽匙。

最近想看看算法方面的東西,便先整理下以前的筆記,發(fā)現關于排序算法這塊以前的筆記太潦草,這幾日重新看書修整一番冕房,現在應該可以看入眼了趁矾,如果還是寫得不好详拙,那是體育老師的鍋。

不變性:在很多算法中,有些條件在算法執(zhí)行時是不變的,這些條件被稱為不變性。如冒泡中蒿辙,out右邊的所有數據項為有序俺叭,在算法的整個運行過程中這個條件始終為真耗跛。(一開始,out右邊數為0)

初級

冒泡排序

執(zhí)行非常慢,概念上最簡單。

int out,in;
for(out = size- 1;out > 1;out--){    //outer loop(backward)
     for(in = 0;in < out;in++){           //inter loop(forward)
        if(a[in] > a[in + 1]){          //out of order?
            swap(in,in + 1);             //swap them
        }
   }
}

在內部循環(huán)中就是一個冒泡腹缩,從頭到尾,比較左右兩個,如果逆序就交換位置,最大的那一項會一直被交換,直到排最后猖闪。1,2比較柑爸,(交換),2,3比較,3和4比較,4和5比較。這里的數字指的是位置竭宰,假如2是5個數據中最大的一個,1和2比較孕豹,不變椅野,2和3比較,二者交換位置妖爷,最大值到了3位置蝶涩,3和4比較,最大值到了4位置絮识,再到5位置绿聘。冒泡,冒上來次舌。
在外層循環(huán)中熄攘,是給內層循環(huán)設置數據比較的最后一項,因為下標大于out的數據項都已經排好序了彼念,是最大的放后面挪圾,不需要再比較了。變量out在每完成一個內層循環(huán)后逐沙,就左移一位哲思。

冒泡的效率: 比較的次數,(N-1) + (N-2) + (N-3) + ...+ 1= N*(N-1)/2,交換的次數取平均NxN/4,這個算法0(N^2)

選擇排序

我覺得名字起得不好吩案,體現不出這個算法的思想(主要是冒泡算法太形象了)棚赔。
思想:比較所有的數據項,取出最小值徘郭,放左邊忆嗜。比較剩下的數據,取最小崎岂,放最左。闪湾。冲甘。。
內層循環(huán)中途样,每一個in的新位置江醇,數據項a[in]和a[min]比較,如果a[in]更小何暇,則min被賦值為in的值陶夜,這里只是下標,沒交換裆站。到一次內層循環(huán)結束条辟,再交換數據項润梯。這樣陋葡,最小數據項就會一直被放在左邊。

比較的次數和冒泡是一樣的,但是交換的次數小撵溃。因為交換數據需要在內存中移動,時間上要多(java語言中粘驰,影響不大椿胯,只是改變引用位置而已)

int out,in,min;
for(out = 0;out<nElems - 1;out++){           //outer loop
   min = out;                                //minimum
   for(in = out + 1;in < nElems;in++){       //inner loop
   if(a[in] < a[min]){                  //if min greater
     min = in;                        //we have a new min
   } 
   swap(out,min);                       //swap them
   }
}

插入排序

思想:假設左邊部分已經排序好了,從某個位置(比如10)開始無序魂爪,將10賦值給一臨時值先舷,然后和前面的數據比較,如果9位置比10大滓侍,就9右移一位蒋川,繼續(xù)和8比較。粗井。尔破。直到到數據的最左邊或找到比10位置數據小的某數據,放在它的右邊浇衬,10位置的數據就排好了懒构。
在大多數情況下,插入算法仍然需要0(N^2)的時間耘擂,但比冒泡快一倍胆剧,比選擇排序也還要快一點。經常被用在較復雜的排序算法的最后階段醉冤,例如快速排序秩霍。

int in,out;
for(out = 1;out < nElems; out++){    //out is dividing line
   long temp = a[out];               //remove marked item
   in = out;                         //start shifts at out
   while(in > 0 && a[in - 1] >= temp){   //until one is smaller,
     a[in] = a[in -1]                //shift item right
     --in;
   }
   a[in] = temp;                   //insert marked item
}

外層的for循環(huán)中,out變量從1開始蚁阳,向右移動,它標記了未排序部分的最左端的數據铃绒。
而在內層while循環(huán)中,in變量從out變量開始螺捐,向左移動颠悬,知道temp變量小于in所指的數組數據項,或者它已經不能再往左移動為止定血。while循環(huán)的每一次都向右移動了一個排序的數據項赔癌。

在每次結束時,在將temp位置的項插入后澜沟,比outer變量下標號小的數據項都是局部有序的灾票。
比較次數,1+2+3+...+(N-1) = Nx(N-1)/2茫虽,而因為每一次排序發(fā)現插入點之前刊苍,平均只有全體數據項的一半真的進行了比較既们,所以是N*(N-1)/2 /2
復制的次數大致等于比較的次數。復制和交換的時間耗費不同班缰。相對于隨機數據贤壁,這個算法比冒泡快一倍,比選擇排序略快埠忘。
對于已經有序或基本有序的數據來說脾拆,插入排序要好得多。對于逆序排列的數據莹妒,每次比較和移動都會執(zhí)行名船,所以插入排序不比冒泡排序快。

中級

歸并排序

只要O(N * logN)旨怠,而冒泡排序渠驼,選擇排序,插入排序都要用O(N * N);.
歸并排序的思想是:將一個數組分成兩半鉴腻,然后分別排序迷扇,然后將數組的兩半合并,合并的時候需要比較大小(合并的時候還要考慮兩小數組還有沒有數據爽哎,即有可能一邊有蜓席,另一邊沒有)。至于如何排序1/2半的數組课锌,當然是再分成兩個1/4數組厨内,再排序。渺贤。雏胃。直到分割的小數組只有1個數據項,不用排序了志鞍。這是用到遞歸的思想
歸并排序的缺點瞭亮,需要在存儲器中有另一個大小等于被排序的數據項數目的空間,用來復制分割出來的小數組固棚。
歸并算法的效率由來:需要復制log2N層(分子數組)街州,每一個層都是N個數據,所以是NxlogN.

int[] workSpace = new int[source.length];
recMergerSort(source,workSpace,0,length-1);

private static void recMergeSort(int[] source ,int[] workSpace,int lowerBound, int upperBound){

    if (lowerBound == upperBound){
       return; // if range is 1,no use sorting
    } else {
       int mid = (lowerBound + upperBound) / 2; //find midpoint
       recMergeSort(source,workSpace,lowerBound,mid); // sort low half
       recMergeSort(source,workSpace,mid + 1,upperBound); // sort high half
       merge(source,workSpace,lowerBound,mid+1 ,upperBound); //merge them
    }
}

private static void merge(int[] source,int[] workPlace, int lowPtr, int highPtr, int upperBound) {

   int j = 0; // workspace index
   int lowerBound = lowPtr;
   int mid = highPtr - 1;
   int n = upperBound - lowerBound + 1; // size of items
   while (lowPtr <= mid && highPtr <= upperBound){
      if (source[lowPtr] < source[highPtr]){
          workPlace[j++] = source[lowPtr++];
      } else {
          workPlace[j++] = source[highPtr++]
      }
   }

   while (lowPtr <= mid){
       workPlace[j++] = source[lowPtr++];
   }

   while (highPtr <= upperBound){
       workPlace[j++] = source[highPtr++];
   }

   for (j = 0;j < n; j++) {
      source[lowerBound + j] = workPlace[j];
   }

} 

高級

希爾排序玻孟,快速排序
希爾排序大約需要O(Nx(logN)^2)時間,快速排序需要O(N*logN)時間鳍征。
而這兩種算法都不需要大量的輔助存儲空間黍翎,不同于歸并算法。
快速排序是所有通用排序算法中最快的一個排序算法艳丛。

希爾排序
希爾排序基于插入排序匣掸,但增加了一個新的特性趟紊,大大地提高了插入排序的執(zhí)行效率。(希爾是個人名碰酝。霎匈。。)
改進的地方:插入算法中送爸,如果一個數據比較小而居于最右邊铛嘱,那么它需要一個一個地移動所有中間的數據項,效率比較低袭厂。
希爾排序通過加入插入排序中元素之間的間隔墨吓,并在這些有間隔的元素中進行插入排序,從而使數據項能大跨度地移動纹磺。當這些數據項排過一趟序后帖烘,減小數據項的間隔。再進行排序橄杨,依次進行下去秘症。間隔被稱為增量,用h表示.
進行過幾次增量排序后式矫,所有的元素離它再最終有序序列中的位置相差不大乡摹,數組達到"基本有序",這時再來插入排序,移動量非常小衷佃。

當h值很大的時候趟卸,數據項每一趟排序需要移動元素的個數很少,但數據項移動的距離很長氏义,這是非常有效率的锄列。當h減少時,每一趟排序需要移動的元素的個數增多惯悠,但是此時數據項已經接近于它們排序后最終的位置邻邮,這對于插入排序可以更有效率。

其中克婶,h = h x 3 +1, h = (h -1) / 3,是經驗值筒严。
java代碼實現:

class ArraySh{

    private long[] theArray;
    private int nElems;

    public ArraySh(int max){
         theArray = new long[max];
         nElems = 0;
    }

    public void insert(long value){
        theArray[nElems] = value;
        nElems++;
    }

    public void shellSort(){
       int inner,outer;
       long temp;
       int h = 1;

       while(h <= nElems / 3){
           h = h * 3 + 1;
       }

       while(h > 0){
          for(outer=h;outer<nElems;outer++){
              temp = theArray[outer];
              inner = outer;

              while(inner > h - 1 && theArray[inner - h] >= temp){
                  theArray[inner] = theArray[inner - h];
                  inner -= h;
              }
               theArray[inner] = temp;
          }
          h = (h-1)/3;
       }
    }

}

劃分算法

劃分是快速排序的根本機制,但是劃分本身也是一個有用的操作情萤,這里單講劃分的算法鸭蛙。
劃分數據就是把數據分為兩組,使所有關鍵字大于特定值的數據項在一組筋岛,使所有關鍵字小于特定值的數據項在另一組娶视。

劃分算法由兩個指針開始工作,兩個指針分別指向數組的兩頭。在左邊的指針肪获,leftPtr寝凌,向右移動,而在右邊的指針孝赫,rightPtr较木,向左移動。
實際上青柄,leftPtr初始化時是在第一個數據項的左邊一位(-1)伐债,rightPtr是在最后一個數據項的右邊一位(size),這是因為在工作之前刹前,它們都要分別的加一和減一泳赋。
停止和交換:當leftPtr遇到比樞紐(特定值,劃分值)小的數據項時喇喉,它繼續(xù)右移祖今,因為這個數據項的位置已經處在數組的正確一邊了。但是拣技,當遇到比樞紐大的數據項時千诬,它就停下來。類似的膏斤,當rightPtr遇到小于樞紐的數據項時徐绑,它也停下來。兩個內層的while循環(huán)莫辨,控制這個掃面過程傲茄,當兩個都停下來時,要么指針到頭要么遇到錯誤的數據(大小比較不對)沮榜,做交換(更換位置盘榨,正確排列了)。
當兩個指針最終相遇的時候蟆融,劃分過程結束草巡,并且推出這個外層的while循環(huán)。

java代碼實現:

class ArrayPar{
    private long[] theArray;
    private int nElems;

    public ArrayPar(int max){
         theArray = new long[max];
         nElems = 0;
    }

    public void insert(long value){
         theArray[nElems] = value;
         nElems++;
    }

    public int partitionIt(int left,int right,long pivot){
         int leftPtr = left - 1;
         int rightPtr = right + 1;

         while(true){
//這里需要檢查邊界型酥,povit是外界設定山憨,對效率影響很大,在快速排序中有更巧妙的設定方法
             while(leftPtr < right && theArray[++leftPtr] < povit);   //find bigger item
             while(rightPtr > left && theArray[--rightPtr] > povit);  //find smaller item
             if(leftPtr >= rightPtr){      //if pointers cross, partition done
                 break;
             } else {
                 swap(leftPtr,rightPtr);
             }
         } 
          return leftPtr;
    }

}

精巧的代碼分析
這個while循環(huán)中的代碼相當精巧弥喉。舉例來說郁竟,想要從內部循環(huán)條件中除去加1操作符,并且用這個加1操作符代替空操作指令語句(空操作指令指只包括一個分號的語句由境,它表示不做任何操作)枪孩。
可以把如下代碼

while(leftPtr < right && theArray[++leftPtr] < pivot);

改為

while(leftPtr < right && theArray[leftPtr] < pivot) { 
    ++leftPtr 
};

這些改變使指針的初始值分別設為left,right,比設為left-1,right+1要更為清晰。
但是蔑舞,這些改變導致只有在滿足條件的情況下指針才會加1.而指針在任何情況下都必須移動,所以空操作指令是最有效的解決辦法嘹屯。

快速排序

最流行的排序算法攻询,時間復雜度為O(N*logN)。雖然不覺得這種行為好州弟,但有的公司喜歡筆試時讓人手寫快排(一些開發(fā)者如是說)钧栖。
原理是:把一個數組劃分為兩個子數組,這里用到劃分算法婆翔,左子數組的數據項都小于右子數組的數據項拯杠,然后遞歸地調用自身為每個子數組進行快速排序來實現,最后使用插入排序啃奴。

在這個算法中劃分的關鍵值(樞紐)的選擇非常重要潭陪。
最初思想,選用數組最右邊的值為pivot,進行一次劃分最蕾,劃分的結果就是left->mid-1, mid->right-1, right(這個位置的值是pivot)依溯,三部分,然后交換mid和right的值(劃分算法的leftPtr在停止時會停在mid位置)瘟则,這樣pivot就到中間黎炉,而小于pivot的值全在左邊,大于的值全在右邊醋拧,數組的排序不受影響慷嗜。
下面的排序從left到pivot-1,pivot+到right。中間項不參與劃分丹壕。

最初思想的java代碼實現:

class ArrayIns{
    private long[] theArray;
    private int nElems; // elements number, or size

    public ArrayIns(int max){
         theArray = new long[max];
         nElems = 0;
    }

    public void insert(long value){
         theArray[nElems] = value;
         nElems++;
    }

    public void quickSort(){
         recQuickSort(0,nElems - 1);
    }

    private void recQuickSort(int left,int right){

         if(right - left <= 0) {          // if size <= 1, already sorted
              return;
         } else {
              long pivot = theArray[right];     // rightmost item
              int partition = partitionIt(left,right,pivot);
             recQuickSort(left,partition - 1);    //sort left side
             recQuickSort(partition+1,right);     //sort right side
         }
    } // end recQuickSort()

    private int partitionIt(int left,int right,long pivot){
       int leftPtr = left - 1;
       int rightPtr = right;    //這里設定最右為pivot庆械,所以從right-1開始劃分,下面代碼會-1

       while(true){
             while(theArray[++leftPtr] < pivot);
             while(rightPtr > 0 && theArray[--rightPtr] > pivot);
             if(leftPtr >= rightPtr){
                 break;
             } else { 
                 swap(leftPtr,rightPtr);
             }
       } // end while(true)

       swap(leftPtr,right);    //restore pivot  當0->right-1劃分好了之后雀费,交換right和leftPtr的位置干奢,將pivot移動到中間,
       return leftPtr;         //return pivot location
    }

}

最初思想中盏袄,使用最右為pivot忿峻,如果數據本身有序,那么pivot會是最小or最大(數組逆序or正序)辕羽,不能起劃分作用逛尚,效率低下。
(為什么不掃描要排序的全部數據刁愿,取中間值绰寞,因為這個做法比排序本身還要費時間,不可行)
進一步優(yōu)化:"三數據項取中(median-of-three)",找數組里第一個滤钱,最后一個觉壶,中間位置數據項的居中數據項值。
三數據取中的一個好處:可以在第二個內部while循環(huán)中取消rightPtr>left的判斷(left是數組的最左件缸,避免rightPtr跑出數組)铜靶,因為三數據取中時,也對三個數據項進行了排序他炊,已經有序争剿。
還有一個好處:對左端,右端中間的數據項排序之后痊末,劃分過程就不需要再考慮這三個數據項了蚕苇。劃分可以從left+1,right-1開始。
再一步提升凿叠,使用三數據項取中劃分方法涩笤,則必須要遵循快速排序算法不能執(zhí)行三個或者少于三個數據項的劃分的規(guī)則。數字3被稱為切割點幔嫂。

本例子中處理小劃分的方法是manualSort()辆它,代碼很簡單,只是排序3個數據項或以下的數據履恩。
處理小劃分的另一個選擇是使用插入排序锰茉,數量小插入排序效率也很高。也可以把切割點設定為別的數切心,推薦使用9.最好的選擇取決于計算機飒筑,操作系統...等。替換掉recQuickSort()中的if(size <= 3) manualSort()绽昏,改用if(size <= 9) insertSort();

class ArrayIns{
    private long[] theArray;
    private int nElems;

    private ArrayIns(int max){
         theArray = new long[max];
         nElems = 0;
    }

    public void insert(long value){
         theArray[nElems] = value;
         nElems++;
    }

    public void quickSort(){
         recQuickSort(0,nElems-1);
    }

    private void recQuickSort(int left,int right){
         int size = right - left + 1;
         if(size <= 3) {
             manualSort(left,right);
         } else {
             long median = median0f3(left,right);
             int partition = partitionIt(left,right,median);
             recQuickSort(left,partition - 1);
             recQuickSort(partition + 1,right);
         }
    }

    private long medianOf3(int left,int right){
         int center = (left + right) / 2;
         if(theArray[left] > theArray[center])  swap(left,center);
         if(theArray[left] > theArray[right])  swap(left,right);
         if(theArray[center] > theArray[right]) swap(center,right);

         swap(center,right - 1);   // put pivot on right
         return theArray[right -1];
    }

    private int partitionIt(int left,int right,long pivot){
         int leftPtr = left;         //right of first elem
         int rightPtr = right -1;    //left of pivot
         while(true){
              while(theArray[++leftPtr] < pivot);
              while(theArray[--rightPtr] > pivot);
              if(leftPtr >= rightPtr ){
                   break;
              } else {
                 swap(leftPtr,rightPtr);
              }
          }
          swap(leftPtr,right-1);
          return leftPtr;
    }

    private void manualSort(int left,int right){
 int size = right - left + 1;
 if(size <= 1) return;
 if(size == 2) {
    if(theArray[left] > theArray[right]) swap(left,right);return;
 } else {   //size is 3
    if(theArray[left] > theArray[right -1]) swap(left,right-1);
    if(theArray[left] > theArray[right])  swap(left,right);
    if(theArray[right-1]> theArray[right])  swap(right-1,right);

 }

    }

}

也可以選擇协屡,對數組整個使用快速排序,不去考慮小于界限的劃分的排序全谤。當快速排序結束時肤晓,數組已經是基本有序了,然后對整個數組應用插入排序认然,也是非巢购叮快,很多專家推薦這種方法卷员。
如盈匾,把界限設為10,那么快速排序到10就結束毕骡,每一塊10個數據里都是無序的削饵,但每一塊之間都有序了岩瘦。再插入排序。

還有一個提升窿撬,消除遞歸启昧。但這是以前計算機性能不好時好用,現在的提升已經不明顯了劈伴。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末箫津,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子宰啦,更是在濱河造成了極大的恐慌,老刑警劉巖饼拍,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赡模,死亡現場離奇詭異,居然都是意外死亡师抄,警方通過查閱死者的電腦和手機漓柑,發(fā)現死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叨吮,“玉大人辆布,你說我怎么就攤上這事〔杓” “怎么了锋玲?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涵叮。 經常有香客問我惭蹂,道長,這世上最難降的妖魔是什么割粮? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任盾碗,我火速辦了婚禮,結果婚禮上舀瓢,老公的妹妹穿的比我還像新娘廷雅。我一直安慰自己,他們只是感情好京髓,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布航缀。 她就那樣靜靜地躺著,像睡著了一般朵锣。 火紅的嫁衣襯著肌膚如雪谬盐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天诚些,我揣著相機與錄音飞傀,去河邊找鬼皇型。 笑死,一個胖子當著我的面吹牛砸烦,可吹牛的內容都是我干的弃鸦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼幢痘,長吁一口氣:“原來是場噩夢啊……” “哼唬格!你這毒婦竟也來了?” 一聲冷哼從身側響起颜说,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤购岗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后门粪,有當地人在樹林里發(fā)現了一具尸體喊积,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年玄妈,在試婚紗的時候發(fā)現自己被綠了乾吻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡拟蜻,死狀恐怖绎签,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情酝锅,我是刑警寧澤诡必,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站屈张,受9級特大地震影響擒权,放射性物質發(fā)生泄漏。R本人自食惡果不足惜阁谆,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一碳抄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧场绿,春花似錦剖效、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至熬拒,卻和暖如春爷光,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背澎粟。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工蛀序, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留欢瞪,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓徐裸,卻偏偏與公主長得像遣鼓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子重贺,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容

  • 概述:排序有內部排序和外部排序骑祟,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大气笙,一次不能容納全部...
    每天刷兩次牙閱讀 3,727評論 0 15
  • Ba la la la ~ 讀者朋友們次企,你們好啊,又到了冷鋒時間潜圃,話不多說抒巢,發(fā)車! 1.冒泡排序(Bub...
    王飽飽閱讀 1,788評論 0 7
  • 概述 排序有內部排序和外部排序秉犹,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大稚晚,一次不能容納全部...
    蟻前閱讀 5,167評論 0 52
  • 文/畫:拾穗 七夕 何良宵 直叫天下有情人為之瘋狂 玫瑰娃娃唯恐天下不亂 滿天飛舞 難怪牛郎織女鵲橋難相會 如此美...
    拾穗的時光閱讀 181評論 2 4
  • 在古龍筆下的江湖里,有個溫暖的人也搓,叫花滿樓赏廓。 花滿樓是個瞎子,景致再美傍妒,不過無邊黑夜幔摸。 世人看來,目不能視物颤练,本該...
    小火樂閱讀 167評論 0 1