??xml version="1.0" encoding="utf-8" standalone="yes"?>久久婷婷色香五月综合激情,狠狠色丁香久久综合五月,国产99久久久国产精免费http://www.shnenglu.com/elva/category/15274.htmlzh-cnTue, 02 Nov 2010 22:36:36 GMTTue, 02 Nov 2010 22:36:36 GMT60QuickSorthttp://www.shnenglu.com/elva/archive/2010/11/02/132155.html叶子叶子Tue, 02 Nov 2010 08:24:00 GMThttp://www.shnenglu.com/elva/archive/2010/11/02/132155.htmlhttp://www.shnenglu.com/elva/comments/132155.htmlhttp://www.shnenglu.com/elva/archive/2010/11/02/132155.html#Feedback0http://www.shnenglu.com/elva/comments/commentRss/132155.htmlhttp://www.shnenglu.com/elva/services/trackbacks/132155.html1、算法思想
         快速排序是C.R.A.Hoare?962q提出的一U划分交换排序。它采用了一U分ȝ{略Q通常U其为分L(Divide-and-ConquerMethod)?
Q?Q?nbsp;  分治法的基本思想
         分治法的基本思想是:原问题分解q个规模更小但结构与原问题相似的子问题。递归地解q些子问题,然后这些子问题的解l合为原问题的解?
Q?Q快速排序的基本思想
         讑ֽ前待排序的无序区为R[low..high]Q利用分L可将快速排序的基本思想描述为:
①分解:  
         在R[low..high]中Q选一个记录作为基?Pivot)Q以此基准将当前无序区划分ؓ左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high]Qƈ使左边子区间中所有记录的关键字均于{于基准记录(不妨Cؓpivot)的关键字pivot.keyQ右边的子区间中所有记录的关键字均大于{于pivot.keyQ而基准记录pivot则位于正的位置(pivotpos)上,它无d加后l的排序?
    注意Q?
         划分的关键是要求出基准记录所在的位置pivotpos。划分的l果可以单地表示?注意pivot=R[pivotpos])Q?
         R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys
                                    其中low≤pivotpos≤high?
②求解:  
          通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序?
③组合:  
         因ؓ?"求解 "步骤中的两个递归调用l束Ӟ其左、右两个子区间已有序。对快速排序而言Q?"l合 "步骤无须做什么,可看作是I操作?
2、快速排序算法QuickSort
    void   QuickSort(SeqList   RQint   lowQint   high)
      {   //对R[low..high]快速排?
          int   pivotposQ?nbsp;  //划分后的基准记录的位|?
          if(low <high){//仅当区间长度大于1时才L?
                pivotpos=Partition(RQlowQhigh)Q?nbsp;  //对R[low..high]做划?
                QuickSort(RQlowQpivotpos-1)Q?nbsp;  //对左区间递归排序
                QuickSort(RQpivotpos+1Qhigh)Q?nbsp;  //对右区间递归排序
            }
        }   //QuickSort
    注意Q?
         为排序整个文Ӟ只须调用QuickSort(RQ?Qn)卛_完成对R[l..n]的排序?nbsp; 

上一?nbsp;  下一?nbsp; 
3、划分算法Partition
Q?Q?nbsp;  单的划分Ҏ
?nbsp;  具体做法
  W一步:(初始?讄两个指针i和jQ它们的初值分别ؓ区间的下界和上界Q即i=lowQi=highQ选取无序区的W一个记录R[i](即R[low])作ؓ基准记录Qƈ它保存在变量pivot中;
  W二步:令j自high起向左扫描,直到扑ֈW?个关键字于pivot.key的记录R[j]Q将R[j])U至i所指的位置上,q相当于R[j]和基准R[i](即pivot)q行了交换,使关键字于基准关键字pivot.key的记录移C基准的左边,交换后R[j]中相当于是pivotQ然后,令i指针自i+1位置开始向x描,直至扑ֈW?个关键字大于pivot.key的记录R[i]Q将R[i]Udi所指的位置上,q相当于交换了R[i]和基准R[j]Q关键字大于基准关键字的记录移C基准的右边,交换后R[i]中又相当于存放了pivotQ接着令指针j自位|j-1开始向左扫描,如此交替改变扫描方向Q从两端各自往中间靠拢Q直至i=jӞi便是基准pivot最l的位置Q将pivot攑֜此位|上完成了一ơ划分?
②一ơ划分过E?
         一ơ划分过E中Q具体变化情c参见动LC?nbsp; 
③划分算法:
    int   Partition(SeqList   RQint   iQint   j)
        {//调用Partition(RQlowQhigh)Ӟ对R[low..high]做划分,
          //q返回基准记录的位置
            ReceType   pivot=R[i]Q?nbsp;  //用区间的W?个记录作为基?nbsp;  '
            while(i <j){   //从区间两端交替向中间扫描Q直至i=j为止
                while(i <j&&R[j].key> =pivot.key)   //pivot相当于在位置i?
                    j--Q?nbsp;  //从右向左扫描Q查扄1个关键字于pivot.key的记录R[j]
                if(i <j)   //表示扑ֈ的R[j]的关键字 <pivot.key
                        R[i++]=R[j]Q?nbsp;  //相当于交换R[i]和R[j]Q交换后i指针?
                while(i <j&&R[i].key <=pivot.key)   //pivot相当于在位置j?
                        i++Q?nbsp;  //从左向右扫描Q查扄1个关键字大于pivot.key的记录R[i]
                if(i <j)   //表示扑ֈ了R[i]QR[i].key> pivot.key
                        R[j--]=R[i];   //相当于交换R[i]和R[j]Q交换后j指针?
              }   //endwhile
            R[i]=pivotQ?nbsp;  //基准记录已被最后定?
            return   iQ?
        }   //partition  

4、快速排序执行过E?
         快速排序执行的全过E可用递归树来描述?
Q图省略Q?
分析Q?nbsp; 
         Q?Q递归执行的\U如图中带箭头的包络U所C?
          Q?Q?nbsp;  递归树上每一l点左旁Ҏ可C当前待排序的区_l点内的关键字是划分的基准关键字
    注意Q?
          叶结点对应的子区间只有一个关键字Q无d分,故叶l点内没有基准关键字
  Q?Q?nbsp;  划分后得到的左、右两个子区间分别标在该l点的左、右两个孩子l点的左Ҏ括号内?
【例】根l点左旁Ҏ号[49Q?8Q?5Q?7Q?6Q?3Q?7Q?9]表示初始待排序的关键字,根内?9表示所选的划分基准记录的关键字Q划分结果是[27Q?8Q?3]49[76Q?7Q?5Q?9_]Q其左右子区间分别标在根l点的两个孩子的左边?
          Q?Q?nbsp;  每个分支l点x圆括号中的内容表C对该结点左旁区间的排序q程l束之后q回的结果。它是其左右孩子对应的区间排序完成之后,左叛_子对应的排序l果分别攑֜该分支结点的关键字前后所得到的关键字序列?
【例】分支结?6的左叛_子对应的区间排序后的l果分别?49_Q?5)?97)Q将它们分别攑֜76的前后即?49Q?5Q?6Q?7)Q这是对l点76左旁区间[76Q?7Q,65Q?9]排序的结果?
          Q?Q?nbsp;  法的执行顺序是递归树中的箭头顺序,实际上当把划分操作视问结点的操作Ӟ快速排序的执行q程相当于是先序遍历光归树?
    注意Q?
         M递归法均可用递归树来描述其执行过E?
5、快速排序各ơ划分后的状态变?
[49   38   65   97   76   13   27   49]   //初始关键?
[27   38   13]   49   [76   97   65   49]   //W?ơ划分完成之后,对应递归树第2?
[13]   27   [38]   49   [49   65]   76   [97]   //对上一层各无序区划分完成后Q对应递归树第3?
13   27   38   49   49   [65]   76   97   //对上一层各无序区划分完成后Q对应递归树第4?
13   27   38   49   49   65   76   97   //最后的排序l果
6、算法分?
         快速排序的旉主要耗费在划分操作上Q对长度为k的区间进行划分,共需k-1ơ关键字的比较?
Q?Q最坏时间复杂度
         最坏情冉|每次划分选取的基准都是当前无序区中关键字最?或最?的记录,划分的结果是基准左边的子区间为空(或右边的子区间ؓI?Q而划分所得的另一个非I的子区间中记录数目Q仅仅比划分前的无序Z记录个数减少一个?
         因此Q快速排序必dn-1ơ划分,Wiơ划分开始时区间长度为n-i+1Q所需的比较次Cؓn-i(1≤i≤n-1)Q故ȝ比较ơ数辑ֈ最大|
                              Cmax   =   n(n-1)/2=O(n2)
         如果按上面给出的划分法Q每ơ取当前无序区的W?个记录ؓ基准Q那么当文g的记录已按递增?或递减?排列Ӟ每次划分所取的基准是当前无序Z关键字最?或最?的记录,则快速排序所需的比较次数反而最多?
Q?Q?nbsp;  最好时间复杂度
         在最好情况下Q每ơ划分所取的基准都是当前无序区的 "中?"记录Q划分的l果是基准的左、右两个无序子区间的长度大致相等。ȝ关键字比较次敎ͼ
                0(nlgn)
注意Q?
         用递归树来分析最好情况下的比较次数更单。因为每ơ划分后左、右子区间长度大致相{,故递归树的高度为O(lgn)Q而递归树每一层上各结Ҏ对应的划分过E中所需要的关键字比较次数d不超qnQ故整个排序q程所需要的关键字比较L数C(n)=O(nlgn)?
         因ؓ快速排序的记录Udơ数不大于比较的ơ数Q所以快速排序的最坏时间复杂度应ؓ0(n2)Q最好时间复杂度为O(nlgn)?
Q?Q基准关键字的选取
         在当前无序区中选取划分的基准关键字是决定算法性能的关键?
  ?"三者取?"的规?
          "三者取?"规则Q即在当前区间里Q将该区间首、尾和中间位|上的关键字比较Q取三者之中值所对应的记录作为基准,在划分开始前该基准记录和该Z的第1个记录进行交换,此后的划分过E与上面所l的Partition法完全相同?
  ②取位于low和high之间的随机数k(low≤k≤high)Q用R[k]作ؓ基准
         选取基准最好的Ҏ是用一个随机函C生一个取位于low和high之间的随机数k(low≤k≤high)Q用R[k]作ؓ基准Q这相当于强qR[low..high]中的记录是随机分布的。用此方法所得到的快速排序一般称为随机的快速排序。具体算法【参见教材?
注意Q?
          随机化的快速排序与一般的快速排序算法差别很。但随机化后Q算法的性能大大地提高了Q尤其是对初始有序的文gQ一般不可能D最坏情늚发生。算法的随机化不仅仅适用于快速排序,也适用于其它需要数据随机分布的法?
Q?Q^均时间复杂度
         管快速排序的最坏时间ؓO(n2)Q但^均性能而言Q它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的^均时间复杂度为O(nlgn)?
Q?Q空间复杂度
         快速排序在pȝ内部需要一个栈来实现递归。若每次划分较ؓ均匀Q则光归树的高度为O(lgn)Q故递归后需栈空间ؓO(lgn)。最坏情况下Q递归树的高度为O(n)Q所需的栈I间为O(n)?
Q?Q稳定?
         快速排序是非稳定的Q例如[2Q?Q?]?


转自Q?br>http://www.360doc.com/content/10/1025/15/4161063_63868950.shtml

叶子 2010-11-02 16:24 发表评论
]]>
shellsort之二http://www.shnenglu.com/elva/archive/2010/11/01/132021.html叶子叶子Mon, 01 Nov 2010 10:08:00 GMThttp://www.shnenglu.com/elva/archive/2010/11/01/132021.htmlhttp://www.shnenglu.com/elva/comments/132021.htmlhttp://www.shnenglu.com/elva/archive/2010/11/01/132021.html#Feedback0http://www.shnenglu.com/elva/comments/commentRss/132021.htmlhttp://www.shnenglu.com/elva/services/trackbacks/132021.htmlhttp://blog.sina.com.cn/s/blog_61e439e50100mfe8.html

希尔排序(shellsort)又叫增量递减(diminishing increment)排序Q是?/font>D.L. Shell发明的,q个法是通过一个逐渐减小的增量一个数l逐渐近于有序从而达到排序的目的Q该法?/font>1959q公布?/font>

最差时间复杂度Q根据步长序列的不同而不同?/font>已知最好的: O(nlog2n)

最优时间复杂度Q?/font>O(n)

q_旉复杂度:Ҏ步长序列的不同而不同?/font>

原始的算法实现在最坏的情况下需要进?/font>O(n2)的比较和交换?/font>V. Pratt的书[1] 对算法进行了量修改Q可以得性能提升?/font>O(n log2 n)。这比最好的比较法?/font>O(n log n)要差一些?/font>

希尔排序通过比较的全部元素分ؓ几个区域来提升插入排序的性能。这样可以让一个元素可以一ơ性地朝最l位|前q一大步。然后算法再取越来越大的步长q行排序Q算法的最后一步就是普通的插入排序Q但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快Q?/font>

假设有一个很的数据在一个已按升序排好序的数l的末端。如果用复杂度ؓO(n2)的排序(冒排序或插入排序)Q可能会q行nơ的比较和交换才能将该数据移x位|。而希排序会用较大的步长Ud数据Q所以小数据只需q行数比较和交换即可到正确位置?/font>

一个更好理解的希尔排序实现Q将数组列在一个表中ƈ对列排序Q用插入排序Q。重复这q程Q不q每ơ用更长的列来进行。最后整个表只有一列了。将数组转换臌是ؓ了更好地理解q算法,法本n仅仅对原数组q行排序Q通过增加索引的步长,例如是用i += step_size而不?/font>i++Q?/font>

例如Q假设有q样一l数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]Q如果我们以步长?/font>5开始进行排序,我们可以通过这列表攑֜?/font>5行的表中来更好地描述法Q这样他们就应该看v来是q样Q?/font>

13 14 94 33 82

25 59 94 65 23

45 27 73 25 39

10

然后我们Ҏ行进行排序:

10 14 73 25 23

13 27 94 33 39

25 59 94 65 82

45

当我们以单行来读取数据时我们得到Q?/font>[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].q时10已经U至正确位置了,然后再以3为步长进行排序:

10 14 73

25 23 13

27 94 33

39 25 59

94 65 82

45

排序之后变ؓQ?/font>

10 14 13

25 23 33

27 25 59

39 65 73

45 94 82

94

最后以1步长q行排序Q此时就是简单的插入排序了)?/font>

步长的选择是希排序的重要部分。只要最l步长ؓ1M步长序列都可以工作。算法最开始以一定的步长q行排序。然后会l箋以一定步长进行排序,最l算法以步长?/font>1q行排序。当步长?/font>1Ӟ法变ؓ插入排序Q这׃证了数据一定会被排序?/font>

法如下

#include <stdio.h>

 

void output_array(int data[], int n)

{

    int i;

    for(i = 0; i < n; i++)

        printf("%d ", data[i]);

    printf("\n");

}

void swap(int *a, int *b)

{

    int x;

    x = *a;

    *a = *b;

    *b = x;

}

void insertion_sort(int data[], int n, int increment)

{

    int i, j;

    for(i = increment; i < n; i += increment)

        for(j = i; j >= increment && data[j] > data[j - increment]; j -= increment)

            swap(&data[j], &data[j - increment]);

}

void shellsort(int data[], int n)

{

    int i, j;

    for(i = n / 2; i > 2; i /= 2)

        for(j = 0; j < i; j++)

            insertion_sort(data + j, n - j, i);

    insertion_sort(data, n, 1);

}

int main()

{

    int data[] = {5, 3, 1, 665, 77, 66, 44, 11, 10, 9, 8, 6};

    output_array(data, 12);

    shellsort(data, 12);

    output_array(data, 12);

    return 0;

}



叶子 2010-11-01 18:08 发表评论
]]>
shellsort之三http://www.shnenglu.com/elva/archive/2010/11/01/132022.html叶子叶子Mon, 01 Nov 2010 10:08:00 GMThttp://www.shnenglu.com/elva/archive/2010/11/01/132022.htmlhttp://www.shnenglu.com/elva/comments/132022.htmlhttp://www.shnenglu.com/elva/archive/2010/11/01/132022.html#Feedback0http://www.shnenglu.com/elva/comments/commentRss/132022.htmlhttp://www.shnenglu.com/elva/services/trackbacks/132022.html|站: JavaEye  作? shenyu  链接Q?http://shenyu.javaeye.com/blog/189563  发表旉: 2008q?5?5?

声明Q本文系JavaEye|站发布的原创博客文章,未经作者书面许可,严禁M|站转蝲本文Q否则必追I法律责任!

插入排序 对基本有序的数组效果非常好,但是对于通常情况则表C般。假设最的数字在最双Q升序排序时Q这个数则要l过nơ交换比较换到最左边。希排序则是对插入排序的很好的修正。而且在希排序很出现最坏状c?/p>

希尔排序通过Ҏl?以一定间隔相隔的位置 q行插入排序Q以辑ֈ让数据快速出现在它应该出现的位置的周_使数l逐步接近基本有序。随着间隔的减,数组来接q基本有序,最后间隔ؓ1Ӟ变成标准的插入排序?/p>

数据的间隔有多种法Q一般要求间隔序列之间互质,此处使用Kunth序列Qh = h * 3 + 1

希尔排序的时间效率很难从理论上证明,实验表明大约是O(n^(3/2)) ~ O(n^(7/6))之间?/p>

代码如下Q?/p>

class Shell {
public static void main(String[] args) {
int[] a = {9,8,7,6,5,4,3,2,1};
sort(a);
println(a);
}
private static void println(int[] a) {
for(int i: a) System.out.print(i + " ");
System.out.println();
}
private static void sort(int[] a) {
int h = 1;
while(h <= a.length/3) h = h * 3 + 1;	//产成Kunth序列
while(h > 0) {
for(int i = h; i < a.length; i++) {	//Ҏ个数据进行间隔ؓh的插入排?
int pos = i;
int temp = a[i];
while(pos >= h && a[pos - h] > temp) {
a[pos] = a[pos-h];
pos -= h;
}
a[pos] = temp;
}
h = (h - 1) / 3;	//减小间隔?
}
}
}
 

叶子 2010-11-01 18:08 发表评论
]]>
shellsort之一http://www.shnenglu.com/elva/archive/2010/11/01/132020.html叶子叶子Mon, 01 Nov 2010 10:06:00 GMThttp://www.shnenglu.com/elva/archive/2010/11/01/132020.htmlhttp://www.shnenglu.com/elva/comments/132020.htmlhttp://www.shnenglu.com/elva/archive/2010/11/01/132020.html#Feedback0http://www.shnenglu.com/elva/comments/commentRss/132020.htmlhttp://www.shnenglu.com/elva/services/trackbacks/132020.htmlhttp://apps.hi.baidu.com/share/detail/15570437


基本思想

先取一个小于n的整数d1作ؓW一个增量,把文件的全部记录分成d1个组。所有距Mؓdl的倍数的记录放在同一个组中。先在各l内q行直接插h排序Q然后,取第二个增量d2<d1重复上述的分l和排序Q直x取的增量dt=1(dt<dt-l<…<d2<d1)Q即所有记录放在同一l中q行直接插入排序为止?/p>

法实现(Java语言)

package org.shirdrn.internal.sort;

/**
* <p><B>希尔排序法c?lt;/B>
* <p>基本思想Q?br>* <p>
* <p>先取一个小于n的整数d1作ؓW一个增量,把文件的全部记录分成d1个组。所有距Mؓdl?br>* 倍数的记录放在同一个组中。先在各l内q行直接插h排序Q然后,取第二个增量d2<d1重复?br>* q的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1)Q即所有记录放在同一l中q行
* 直接插入排序为止?br>* <p>
* <p>该方法实质上是一U分l插入方法?br>*
* @author shirdrn
*
*/
public class ShellSort {

private Integer[] array;

public ShellSort(Integer[] array) {
   this.array = array;
}

public void sort() {
   int d = array.length;
   do {
    d /= 2;
    shellPass(d); // Ҏ逐渐减小的间隔增量,循环调用一排?br>   }while(d>1);
}

/**
* 希尔一排?br>*
* @param d 间隔增量
*/
private void shellPass(int d) {
   Integer tmp;
   for(int i=d; i<array.length; i++) { // 数组下标?开始,初始i=d表示一排序中W二个元?br>    tmp = array[i]; // array[i]的拷?br>    // 如果待处理的无序区第一个元素array[i] < 有序区最大的元素array[i-d]
    // 需要将有序区比array[i]大的元素向后Ud
    if(array[i]<array[i-d]) {
     int j=i-d;
     while(j>=0 && tmp<array[j]) {
      array[j+d] = array[j]; // 左侧有序区中元素比array[i]大的array[j+d]后移
      j -= d;
     }
     // 如果array[i] >= 左侧有序区最大的array[i-d]Q或者经q扫描移动后Q找C个比array[i]的元素
     // 右侧无序区W一个元素tmp = array[i]攑ֈ正确的位|上
     array[j+d] = tmp;
    }
   }
}

/**
* 输出数组元素
*/
public String print() {
   StringBuffer sb = new StringBuffer();
   for(int i=0; i<array.length; i++) {
    sb.append(array[i]);
    if(i != array.length-1) {
     sb.append(", ");
    }
   }
   return sb.toString();
}
}

排序q程

希尔排序的过E如下:

首先初始化间隔d为待排序数组的长度,无需排序?/p>

减小dQ对于每ơ得到的间隔dQ执行多l排序,使得原始数组间隔为d的一个子数组为有序,该数l通过cM直接插入排序的算法来执行排序?/p>

直到Qd减小?的时候,整个数组为有序。这里,采用二分的策略来得到间隔d?/p>

执行希尔排序的过E示例如下:

假设待排序数lؓarray = {94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49}Q数l大ؓ20?/p>

首先Q初始化d = 20。在循环中反复得到间隔dQ根据d执行一希排序?/p>

对于d = 20/2 = 10Q?/p>

Ҏd = 10来对数组排序Q将原始数组分成2块: {94,12,34,76,26,9,0,37,55,76}与{37,5,68,83,90,37,12,65,76,49}Q也是对如下数l分别进行直接插入排序:

{array[0],array[10]} = {94,37}

{array[1],array[11]} = {12,5}

{array[2],array[12]} = {34,68}

{array[3],array[13]} = {76,83}

{array[4],array[14]} = {26,90}

{array[5],array[15]} = {9,37}

{array[6],array[16]} = {0,12}

{array[7],array[17]} = {37,65}

{array[8],array[18]} = {55,76}

{array[9],array[19]} = {76,49}

W一希排序后Q各个子数组变ؓQ?/p>

{37,5,34,76,26,9,0,37,55,49}与{94,12,68,83,90,37,12,65,76,76}Q?/p>

卻Iarray = {37,5,34,76,26,9,0,37,55,49,94,12,68,83,90,37,12,65,76,76}Q?/p>

对于d = 10/2 = 5Q?/p>

Ҏd = 5来对数组排序Q将W一希排序后的数l分??Q{37,5,34,76,26}、{9,0,37,55,49}、{94,12,68,83,90}与{37,12,65,76,76}Q也是对如下数l分别进行直接插入排序:

{array[0],array[5],array[10],array[15]} = {37,9,94,37}

{array[1],array[6],array[11],array[16]} = {5,0,12,12}

{array[2],array[7],array[12],array[17]} = {34,37,68,65}

{array[3],array[8],array[13],array[18]} = {76,55,83,76}

{array[4],array[9],array[14],array[19]} = {26,49,90,76}

W二希排序后Q各个子数组变ؓQ?/p>

{9,0,34,55,26}、{37,5,37,76,49}、{37,12,65,76,76}与{94,12,68,83,90}Q?/p>

卻Iarray = {9,0,34,55,26,37,5,37,76,49,37,12,65,76,76,94,12,68,83,90}?/p>

对于d = 5/2 = 2Q?/p>

Ҏd = 2来对数组排序Q将W二希排序后的数l分?0块: {9,0}、{34,55}、{26,37}、{5,37}、{76,49}、{37,12}、{65,76}、{76,94}、{12,68}与{83,90}Q也是对如下数l分别进行直接插入排序:

{array[0],array[2],array[4],array[6],array[8],array[10],array[12],array[14],array[16],array[18]} = {9,34,26,5,76,37,65,76,12,83}

{array[1],array[3],array[5],array[7],array[9],array[11],array[13],array[15],array[17],array[19]} = {0,55,37,37,49,12,76,94,68,90}

W三希排序后Q各个子数组变ؓQ{5,0}、{9,12}、{12,37}、{26,37}、{34,49}、{37,55}、{65,68}、{76,76}、{76,90}与{83,94}Q?/p>

卻Iarray = Q{5,0,9,12,12,37,26,37,34,49,37,55,65,68,76,76,76,90,83,94}?/p>

对于d = 2/2 = 1Q?/p>

Ҏd = 1来对数组排序Q将W二希排序后的数l分?0块:{5}、{0}、{9}、{12}、{12}、{37}、{26}、{37}、{34}、{49}、{37}、{55}、{65}、{68}、{76}、{76}、{76}、{90}、{83}、{94}Q也是对如下数l分别进行直接插入排序:

{5,0,9,12,12,37,26,37,34,49,37,55,65,68,76,76,76,90,83,94}

W四希排序以后,数组已经有序Q?/p>

array = {0,5,9,12,12,26,34,37,37,37,49,55,65,68,76,76,76,83,90,94}?/p>

因ؓ d= 1Q希排序结束?/p>

试用例

package org.shirdrn.internal.sort;

import junit.framework.TestCase;

public class TestShellSort extends TestCase {

private ShellSort sort;
private Integer[] array;

@Override
protected void setUp() throws Exception {
   array = new Integer[]{
     94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49
   };
   sort = new ShellSort(array);
}

public void testSort() {
   // B(Before),A(After)
   System.out.println("(B)Sorting : " + this.sort.print());
   this.sort.sort();
   System.out.println("(A)Sorting : " + this.sort.print());
}
}

试l果Q?/p>

(B)Sorting : 94, 12, 34, 76, 26, 9, 0, 37, 55, 76, 37, 5, 68, 83, 90, 37, 12, 65, 76, 49
(A)Sorting : 0, 5, 9, 12, 12, 26, 34, 37, 37, 37, 49, 55, 65, 68, 76, 76, 76, 83, 90, 94

法分析

Q一Q时间复杂度

Shell排序的执行时间依赖于增量序列?/p>

好的增量序列的共同特征:

?最后一个增量必Mؓ1Q?/p>

?应该量避免序列中的?其是相ȝ?互ؓ倍数的情c?/p>

有h通过大量的实验,l出了目前较好的l果Q当n较大Ӟ比较和移动的ơ数U在nl.25?.6n1.25之间?/p>

Q二Q空间复杂度

因ؓ希尔排序依赖于增量序列,从而导致排序的数不固定,对于不同的增量执行一希排序,只用C个辅助变量?/p>

Q三Q排序稳定?/p>

通过上述元素76可以看到Q希排序不E_?/p>

因此Q希排序是不稳定的?/p>



叶子 2010-11-01 18:06 发表评论
]]>
谈谈 Hash Tablehttp://www.shnenglu.com/elva/archive/2010/10/25/131186.html叶子叶子Mon, 25 Oct 2010 04:57:00 GMThttp://www.shnenglu.com/elva/archive/2010/10/25/131186.htmlhttp://www.shnenglu.com/elva/comments/131186.htmlhttp://www.shnenglu.com/elva/archive/2010/10/25/131186.html#Feedback0http://www.shnenglu.com/elva/comments/commentRss/131186.htmlhttp://www.shnenglu.com/elva/services/trackbacks/131186.htmlhttp://geeklu.com/2010/07/hash-table/

 

一.数据l构

在我们编E的世界里数据的基本l织可以说有三种形式?/p>

  1. l构?或对?
  2. 数组
  3. 链表

其他M的数据组lŞ式都可以看作是这三种数据l织形式的组合变体?br>
l构?或对?可以是基本数据类型或者其他结构体(或对?的组合。结构体或对象一般用来描qC个复杂数据实体?/p>

数组一般是一l同cd的变量的集合Q在内存中表Cؓ一片连l的I间Q因为空间是q箋的,且每一个数据单元占的内存空间的大小是相{的Q所以可以根据地址的偏Ud数据元素实现快速访问,但是当需要插入或者删除一个元素的时候,则需要对目标元素的之后的所有元素进行移动了?/p>

链表的单个节点一般ؓl构体或者对象,因ؓ链表的单个节炚w了需要保存数据之外还需要维护它的相邻节点的关系Q如果想获得链表中的某个节点的|需要从链表的头l点开始遍历,直到扑ֈ需要的东西Q而插入或者删除某个节点的话,需要找到相应的节点Q修改其以及其相邻节点的相关指针的引用即可?/p>

像其他的数据l构Q比?队列Q栈Q树Q都可以通过数组或者链表来l织Qƈ实现相应的操作功能?/p>

?Hash Table

q个世界上没有十全十的东西Q所以我们要学会取舍。Q何技术的实现都没有最好的只要最合适的Q也p实现的最x案是和应用场景息息相关的?br>很多时候,我们惛_数据q行快速的存取Q比如缓存的实现Q,q用一个key来标记自己存取的数据。我们可以把它叫做key-value的结构?br>说到“快?#8221;我们很快惛_数组Q因为数l可以在O(1)的时间复杂内完成指定位置元素的读写操作?br>所以在理想状态,如果一个数l够长Q且存在一个函数可以将每一个key映射到唯一的一个数l下标,那么我们可以很完美的解决问题。但往往资源都是有限的,我们没有那么大的I间Q也不能设计一个无比负责的映射法保证每一个key对应C个唯一的数l下标。所以我们会选择一些折中的Ҏ?/p>

hash table便是册c问题而存在的?/p>

1.哈希函数

Hash或者你可以译成散列或者杂凑,hash操作其本质上是一个数据映成另一个数据,通常情况下原数据的长度比hash后的数据定w大?br>q种映射的关pL们叫做哈希函数?br>
一般情况下 哈希函数的输入可能的L要远q多于哈希值所能表C的LQ所以就有可能两个不同的输入对应同一个哈希|通常把具有不同关键码而具有相同哈希值的记录UC“同义?#8221;?br>在信息安全领域中也经怋用到哈希函数Q不q需要用的是单向哈希函敎ͼ是无法通过哈希的结果反推出输入Q所以经常应用于密码的加密,传输内容的完整性检查,在安全领域常用的哈希法?MD5QSHA1{?br>在哈希表的应用中Q哈希函数常用余数法q行Q也是通过求模的方式算出哈希倹{?/p>

2.哈希?/h3>

哈希表是一U数据结构,实现key-value的快速存取。之前说q数l可以实现快速存取,所以哈希表肯定会用到数组。在q里Q我们把每一个数l的单元叫做一个bucketQ桶Q?/p>

构造哈希函?/h5>

q里哈希函数的作用就是将key映射C个存储地址。所以构造一个哈希表我们得先构造哈希函数?br>如果一个key哈希后对应地址中已l存放了gQ这U情冉|们叫做哈希冲H(Hash collisionsQ?br>如果存在一个哈希函敎ͼ使得每一个输入都能对应到唯一的一个存储单元中Q没有冲H)Q那么这L哈希函数我们可以叫它完美哈希函数QPerfect Hash FunctionQ简UPHF)?br>但ؓ了哈希函数简单,q行速度快,往往不会使用完美哈希函数。所以冲H肯定会存在的,Z减少冲突Q我们希望哈希函数的l果均匀的分布在地址单元的空间中。这样可以有效的减少冲突?br>
装填因子Load factor a=哈希表的实际元素数目(n)/ 哈希表的定w(m) a大Q哈希表冲突的概率越大,但是a接q?Q那么哈希表的空间就浪贏V?br>一般情况下Load factor的gؓ0-0.7QJava实现的HashMap默认的Load factor的gؓ0.75Q当装蝲因子大于q个值的时候,HashMap会对数组q行扩张臛_来两倍大?/p>

冲突解决

既然冲突不可避免Q那么我们就必须对冲H进行解?M能把之前的内容覆盖掉?,
解决冲突的方式主要分两类
开攑֮址?Open addressing)q种Ҏ是在计一个key的哈希的时候,发现目标地址已经有gQ即发生冲突了,q个时候通过相应的函数在此地址后面的地址LQ直到没有冲Hؓ止。这个方法常用的有线性探,二次探测Q再哈希?br>q种解决Ҏ有个不好的地方就是,当发生冲H之后,会在之后的地址I间中找一个放q去Q这样就有可能后来出C个key哈希出来的结果也正好是它放进ȝq个地址I间Q这样就会出现非同义词的两个key发生冲突?br>

链接?Separate chaining)链接法是通过数组和链表组合而成的。当发生冲突的时候只要将其加到对应的链表中即可?/p>

与开攑֮址法相比,链接法有如下几个优点Q?br>①链接法处理冲突单,且无堆积现象Q即非同义词决不会发生冲H,因此q_查找长度较短Q?br>②由于链接法中各链表上的l点I间是动态申LQ故它更适合于造表前无法确定表长的情况Q?br>③开攑֮址法ؓ减少冲突Q要求装填因?#945;较小Q故当结点规模较大时会浪费很多空间。而链接法中可?#945;≥1Q且l点较大Ӟ拉链法中增加的指针域可忽略不计,因此节省I间Q?br>④在用链接法构造的散列表中Q删除结点的操作易于实现。只要简单地删去链表上相应的l点卛_。而对开攑֜址法构造的散列表,删除l点不能单地被删结点的I间|ؓI,否则截断在它之后填人散列表的同义词l点的查找\径。这是因为各U开攑֜址法中Q空地址单元(卛_攑֜址)都是查找p|的条件。因此在 用开攑֜址法处理冲H的散列表上执行删除操作Q只能在被删l点上做删除标记Q而不能真正删除结炏V?/p>

当然链接法也有其~点Q拉链法的缺ҎQ指针需要额外的I间Q故当结点规模较时Q开攑֮址法较省空_而若节省的指针I间用来扩大散列表的规模Q可使装填因子变,q又减少了开攑֮址法中的冲H,从而提高^均查N度?/p>


?部分囄来自Wikipedia

叶子 2010-10-25 12:57 发表评论
]]>
þþƷһ| þպƬ| Ʒһþ| ŷƷƵһþþþƷ | avþþƷ| þ99Ʒþþþþhb| ݺ88ۺϾþþþۺ | AVӰƬþþþþ | ˾Ʒþһ | ݺɫþۺ| þþþùƷ| þþŮһ| ƷþþþþþþþĻ | ƷѾþþþþþþ | þþƷֻоƷ66| þ޾ƷAV| һaƬþëƬ| ŷþþþƷ| þù׽ | þþһƷ99þþƷ66| þøƬ| ѹۿ˾þѹۿ| AVһþ| ŷһþþ| ݺ޾þþþþۺ| þþƷһ| ݺɫþþۺ| þþþþҹƷƷ| պŷۺϾþ| 2021ھƷþþþþӰԺ| ޾ƷWWWþþþþ| þþþ18| ޾þһ| ŷձþùʵҶ԰| þ99ëƬѹۿ| ھƷþù½| þõӰ| þþþƷձһ| þþþþùaѹۿ| þ޹˾Ʒ| þþþùһ|