第八章:数据结构排序(插入排序+选择排序+交换排序+分配排序+归并排序)

目录

一、插入排序:

直接插入排序

二分法插入排序

表插入排序

Shell排序(每组自己排序)

二、选择排序

堆排序

三、交换排序

1.起泡排序

​编辑

2.快速排序(分区交换排序)

四、 分配排序

 五、归并排序


一、插入排序:

每步将一个待排序的记录, 按其排序码大小, 插入到前面已经排好序的文件中的适当位置, 直到全部记录都被插入完为止

排好序的序列+待排序的序列

/*插入排序*/ 
typedef int KeyType;
typedef int DataType;

typedef struct
{
    KeyType key;      //排序码字段
	DataType info;    //记录的其他字段	
};RecordNode; 

typedef struct
{
	int n;            //文件中的记录个数 
	RecordNode * record;//记录的指针字段 
}SortObject;

直接插入排序

/*插入排序:直接插入排序*/
void insertSort ( SortObject *pvector )
 {
    int i, j;
    RecordNode temp;
    RecordNode *data = pvector->record; 
    for( i = 1; i < pvector->n; ++i) 
    { 
         temp = data[i]; 
         for ( j = i-1; temp.key < data[j].key && j >= 0; j--)
            data[j+1] = data[j];
        if( j != i-1 ) data[j+1] = temp;
    }
}

二分法插入排序

/*二分法插入排序*/ 
void binSort ( SortObject * pvector )
 {
    int i, j, left, mid, right; 
    RecordNode temp;
    RecordNode *data = pvector->record; 
    for( i = 1; i < pvector->n; i++ )
    {   temp = data[i];   
	    left = 0;    
		right = i –1;    //区间:left-right 
        
		while (left <= right)
        {   
           mid=(left+right)/2;  
           if (temp.key<data[mid].key) //小于中间 
		        right=mid-1;           //右侧 
           else 
		       left=mid+1;
       }
      for( j=i-1; j>=left; j-- )  data[j+1] = data[j];
      if ( left != i )  data[left] = temp;
  }
} 

 

表插入排序

在直接插入排序的基础上减少记录移动的次数

单链表代替顺序表(插入记录Ri时,R0至Ri-1已排好序, 先将Ri脱链, 采用顺序比较的方法找到Ri应插入的位置,将Ri插入链表)

/*表插入排序*/
struct Node;
typedef struct Node ListNode;//链式

struct Node {
  KeyType key; //记录的排序码字段
  DataType info; //记录的其他字段
  ListNode *next; //记录的指针字段
};

typedef ListNode *LinkList; 

void listSort ( LinkList * plist ) 
{
    ListNode *now, *pre, *p, *q, *head;
    head=*plist;   
    pre=head->next;     if (pre==NULL) return;
    now=pre->next;      if(now==NULL) return;
   while ( now!=NULL ) {
      q=head;    p=head->next;
      while ( p!=now && p->key<=now->key )
      {  q=p;  p=p->next;  }
     if ( p==now ) { pre=pre->next;  now=pre->next;  continue ;  } 
     pre->next=now->next;
     q->next=now;   now->next=p;
     now=pre->next;
  }
} 

Shell排序(每组自己排序)

把一个序列看成由间距 d 的元素构成的 d 个“ 子序列 ”。反复做:
¤ 在每个子序列内部分别排序
¤ 缩小 d 之后重复第 1 直到 d 缩小为 1 时再做一次排序 ( 即对整个序列排序 )
/*shell排序*/
inc = d; 
while ( inc > 0 )
{ 
   对相距inc 的各组排序;
   inc = decrease (inc) ;
}; 

//开始
void shellSort ( SortObject * pvector, int d ) 
{
   int i, j, inc; 
   RecordNode temp, *data = pvector->record; 

   for (inc = d; inc > 0; inc /= 2) {
     for (i = inc; i < pvector->n; i++) { 
          temp = data[i]; 
          for (j = i -inc; j >= 0 && temp.key < data[j].key; j -= inc)
               data[j+inc] = data[j]; 
          data[j+inc] = temp;
      }
   }
}


二、选择排序

基本思想:

维护最小的i 个记录的已排序序列

每次从剩余未排序的记录中选取关键码最小的记录,排在已排序序列之后,作为序列的第i +1 个记录

直接选择排序

空排序序列开始,每次从未排序记录中选排序码最小的记录,

与未排序段的第一个记录交换,直到所有记录排好序

PS:比较次数与文件初始状态无关

void selectSort ( SortObject * pvector )  { 
    int i, j, k;
    RecordNode temp, *data = pvector->record;
    for( i = 0; i < pvector->n-1; i++ ) {
       k = i; 
      for (j = i+1; j < pvector->n; j++) 
         if (data[j].key < data[k].key)    
             k = j;
      if (k != i) 
      {   temp = data[i];   data[i] = data[k];  data[k] = temp;   } 
   }
}

堆排序

¨ 直接选择排序效率较低,第i 趟排序需要作 n- i 次比较,没有利用已做的比较 结果

把待排序的记录构造成堆,然后通过从堆中不断选出最大/小元素

n个元素的序列K={ k0, k1, …, kn-1 }

堆与完全二叉树的顺序表示:

  • 大根堆:kik2i+1kik2i+2

每个二叉树的结点大于等于其左、右孩子结点。即根结点最大

  • 小根堆:kik2i+1kik2i+2 ( i=0,1,,n/2-1) (优先队列中使用)

每个二叉树的结点均小于等于左、右孩子结点。即根结点最小

void heapsort ( SortObject  *pvector ) { 
   int i, n;
   RecordNode temp;
   n=pvector->n;
  for ( i=n/2-1; i>=0; i--) 
     sift(pvector,n,i);
  for(i=n-1; i>0; i--) {
        temp=pvector->record[0];
        pvector->record[0]=pvector->record[i];
        pvector->record[i]=temp;
        sift(pvector,i,0);
    }
} 


//将以pvector->record[p]为根的子树调整为大根堆
void sift ( SortObject * pvector, int size , int p ) {
   RecordNode temp=pvector->record[p];
    int child= 2* p +1; 
    while (child<size)  {
       if( ( child<size-1 )&& ( pvector->record[child].key<pvector->record[child+1].key ) )
            child++;
       if( temp.key < pvector->record[child].key ) {
           pvector->record[p]=pvector->record[child]; 
           p=child;   child= 2*p +1;
       }
      else break;
    }
    pvector->record[p]=temp;
} 

三、交换排序

依次比较相邻数据项,若某两项次序颠倒逆序),则交换它们。重复这一过程直到不需要交换为止

1.起泡排序

相邻元素进行两两比较交换,进而得到一个有序序列

总的比较移动次数比较多,相邻两个记录比较和交换,每次交换只能上移或者下移一个位置

2.快速排序(分区交换排序)

设法把待排序序列按某种标准分为大小两组。例如选择第一条记录R0作为区分标准,将所有大于R0的记录移到R0右边, 所有小于R0的记录移到R0左边

递归地分别对两组记录采用同样方式排序,直至划分到每个子部分只包含一个记录,整个序列的排序完成

【例题一】

 

【例题二】 

 

 

void quicksort ( SortObject *pvector,  int l,  int r )
{
     int   i,  j;
     RecordNode  temp, *data = pvector->record;
     if ( l >= r )   return; //左边小不要动
     i= l;    j= r;    temp = data[i]; 
     while( i != j )              //i=j循环终止 
 {
          while( i< j && data[j].key >= temp.key)   j--; //右边大
          if ( i< j ) data[i++] = data[j]; 
         while( i< j && data[i].key <= temp.key)   i++; //左边 
          if (i< j) data[j--] = data[i]; 
       }
      data[i] = temp;         
      quicksort ( pvector, l,  i-1 );
      quicksort ( pvector,i+1, r );  
}

四、 分配排序

struct Node; 
typedef struct Node *PNode;
struct Node { 
    KeyType key[D];   //D位排序码
    DataType info;
    PNode next;
};
typedef struct { 
     PNode f;    //队列的头指针
     PNode e;   //队列的尾指针
} Queue; 
typedef struct Node *RadixList;    //待排序文件类型

void radixSort( RadixList *plist, int d, int r) { //plist指向带表头结点的单链表的头指针
    int i, j, k;     PNode   p, head = (*plist)->next;
    Queue  queue[r]; //队列数组
    for ( j = d-1; j >= 0; j-- ) {   //进行d趟分配和收集
         for ( i = 0; i < r; i++)   queue[i].f = queue[i].e = NULL;   //队列初始化
         for ( p = head; p != NULL; p = p->next)  {
                k = p->key[j]; //按排序码的第j个分量分配
                if ( queue[k].f == NULL ) queue[k].f = p; 
                else (queue[k].e)->next = p; 
                queue[k].e = p;
          }
         for (i = 0;  queue[i].f == NULL;  i++) ; //找到第一个非空队列
         p = queue[i].e;    head = queue[i].f; //head为收集链表的头指针
         for( i++;  i < r; i++) //收集其他非空队列
              if (queue[i].f != NULL) {   p->next = queue[i].f;  p = queue[i].e;  } 
  
         p->next = NULL;
     }
    (*plist)->next = head;
}

 五、归并排序

¨ 归并排序策略
¤ 把待排序的文件分成 若干个子文件 ,先将 每个子文件 内的记录排序,再将已排序的子文件合并,逐步得到完全排序的文件
¨ 路归并排序
¤ 把待排序序列的 n 个记录看成 n 个有序子序列 , 每个子序列的长度为 1
¤ 把当前的有序子序列两两归并 , 得到两倍长度 数目减半的一组有序子序列
¤ 重复做子序列的两两归并 , 最终得到长度为 n 的有序序列
/*两组有序子序列归并算法*/
//设r[low ]到r [m]和r[m+1 ]到r[high]是两个有序子序列,将其归并到r1[low ]到r1[high] 中

void merge ( RecordNode  *r,  RecordNode  *r1, int low,  int m,  int high ) {
     int i = low, j = m + 1, k = low;

     while ( i <= m && j <= high ) {
        if (r[i].key <= r[j].key)  
            r1[k++] = r[i++];
        else   
            r1[k++] = r[j++];
      } 

      while (i <= m)  r1[k++] = r[i++];
      while (j <= high)  r1[k++] = r[j++];
}
/*一趟二路归并算法*/
//对r 做一趟归并, 结果放入r1; 当前有序段长length 
void mergePass ( RecordNode *r, RecordNode *r1, int n, int length) {
     int i = 0, j;

   //归并长length的两个子段
   while ( i + 2*length -1 < n ) {
       merge(r, r1, i, i+length-1, i + 2*length -1);
       i += 2*length;
    }

    if (i + length -1 < n -1) //剩下两段,后段长度小于length 
        merge(r, r1, i, i+length-1, n-1);
    else //剩下一段,将它复制到数组r1 
        for(j = i; j < n; j++) 
            r1[j] = r[j];
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
插入排序交换排序是两种常见的排序算法,它们的核心思想和操作方式有所不同。 插入排序(Insertion Sort)是一种简单直观的排序算法,其基本思想是将待排序的元素逐个插入已经排好序的序列中的合适位置,从而得到一个有序序列。具体步骤如下: 1. 从第一个元素开始,认为该元素是已经排好序的。 2. 取下一个元素,在已经排好序的元素中从后往前扫描。 3. 如果该元素大于已排序元素,则将已排序元素后移一位。 4. 重复步骤3,直到找到合适的位置将当前元素插入。 5. 重复步骤2-4,直到所有元素都被插入到正确的位置。 交换排序(Exchange Sort)是一类排序算法,其中最著名的是冒泡排序(Bubble Sort)。它通过相邻元素之间的比较和交换来达到排序的目的。具体步骤如下: 1. 从第一个元素开始,比较相邻的两个元素。 2. 如果顺序不正确,则交换这两个元素的位置。 3. 继续比较下一对相邻元素,重复步骤2。 4. 对每一轮循环,最大(或最小)的元素会排到末尾。 5. 重复步骤1-4,直到所有元素都被排序插入排序交换排序的区别在于它们的核心操作方式。插入排序通过将元素逐个插入已排序序列的合适位置来构建有序序列,而交换排序则通过相邻元素的比较和交换来逐步调整元素的位置。这两种算法的时间复杂度都为O(n^2),但在实际应用中,插入排序通常比交换排序具有更好的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值