排序详解

排序详解

一、选择排序

代码:

void SelectSort(int* arr,int len)
{
  int begin = 0;
  int end = len - 1;
  while(begin < end)
  {
    int min_index = begin;
    int max_index = end;
    for(int i = begin;i <= end;i++)
    {
      if(arr[i] > arr[max_index])
      {
        max_index = i;
      }
      if(arr[i] < arr[min_index])
      {
        min_index = i;
      }
    }

    Swap(&arr[begin],&arr[min_index]);

    //交换的时候需要注意,如果最大值刚好在begin位置处,经过前面的交换,最大值已经杯换到最小下标处
    if(max_index == begin)
    {
      max_index = min_index; 
    }

    Swap(&arr[end],&arr[max_index]); 
    begin++;
    end--;
  }
}

在这里插入图片描述
思想:

  • 上述代码的选择排序是先定义两个下标min_index,和max_index,用来记录每趟比较中最小元素和最大元素的下标
  • 然后在循环内部进行选择记录,每趟循环结束min_index和max_index已经记录该趟的最大元素和最小元素的下标,
  • 然后进行交换,把最小的元素交换到数组的begin位置处,最大的元素交换到end位置处,
  • 注意⚠️在交换完最小元素后需要进行判断,因为如果刚好最大元素就是在begin位置处,那么后序要交换最大元素到end位置处就会出错,因为如果最大元素在begin的情况下,经过前面的交换,现在最大元素的位置应该在min_index下标处,所以应更新最大元素位置的下标
  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

二、插入排序

代码:

void InsertSort(int* arr,int len)
{
  int end = 0;

  for(end =0 ;end < len -1;end ++)
  {
    int temp = arr[end +1];
    while(end >= 0)
    {
      if(arr[end] > temp)
      {
        arr[end + 1] = arr[end];
        end--;
      }
      else
      {
        break;
      }
    }
    arr[end + 1] = temp; 
  }
}

在这里插入图片描述
思想:

  • 先定义end来标识有序序列的最后一个元素的下标,
  • 再把end后的第一个元素temp往有序序列内插
  • 如果temp比end位置元素大,就直接往end+1位置放temp即可
  • 如果temp比end位置元素小,就把end位置元素放到end+1位置处,继续循环向前判断
  • 元素集合越接近有序,直接插入排序算法的时间效率越高
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1),它是一种稳定的排序算法
  • 稳定性:稳定
  • 当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与
    array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

三、希尔排序

代码:

void ShellSort(int* arr,int len)
{
  int gap = len;
  while(gap > 1)
  {
    gap = gap / 3 +1;
    for(int i = 0;i < len - gap;i++)
    {
      int end = i;
      int temp = arr[end + gap];
      while(end >= 0)
      {
        if(arr[end] > temp)
        {
          arr[end + gap] = arr[end];
          end -= gap;
        }
        else
        {
          break;
        }
      }
      arr[end + gap] = temp;
    }
  }
}

在这里插入图片描述
思想:

  • 定义一个gap为分得的组数,gap越大越不接近有序,所以gap应是动态变化的,直到为1,就是插入排序
  • 希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个
    组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工
    作。当到达=1时,所有记录在统一组内排好序
  • 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N1.3—N2)
  • 稳定性:不稳定

四、堆排

代码:

//向下调整
void adjust(int* arr,int len,int index)
{
  int parent = index;
  int child = parent * 2 + 1;
  while(child < len)
  {
    if(child + 1 < len && arr[child] < arr[child + 1])
    {
      child = child + 1;
    }
    if(arr[child] > arr[parent])
    {
      Swap(&arr[child],&arr[parent]);
      parent = child;
      child = parent * 2 + 1;
    }
    else
    {
      break;
    }
  }
}

//堆排
void HeapSort(int* arr,int len)
{
  for(int i = (len - 1 - 1 ) / 2;i >= 0;i--)
  {
    adjust(arr, len, i);
  }

  int end = len - 1;

  while(end > 0)
  {
    Swap(&arr[0],&arr[end]);
    adjust(arr,end,0);
    end--;
  }
}

在这里插入图片描述
思想:

  • 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆
  • 注意向下调整算法的写法,首先从倒数第一个非叶子节点开始调整,把整棵树调整为大根堆
  • 建大堆使,取左右两个孩子中较大的和父亲节点比较,看是否需要交换,直到把整棵树调整为大根堆
  • 排序的过程就是把堆顶元素和数组的最后一个元素进行交换,再次进行向下调整,注意此时,交换过来的堆顶元素就相当于是有序的,再次进行向下调整时就把数组的元素个数-1,一次类推,循环排序
  • 堆排序使用堆来选数,效率就高了很多。
  • 时间复杂度:O(N*logN)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

五、冒泡排序:

这个比较简单,直接来代码:

void bubbleSort(int *str, int len)
{
//冒泡排序, 升序排列
//数组当中比较小的数值向下沉,数值比较大的向上浮!
	int i = 0;//趟数
	int flg ;
	for(i = 0;i < len-1;i++)
	{
		int j = 0;//次数
		flg = 0;//如果第一次已经是排序好了,就不用排序了,直接brek
		for(j = 0;j < len-1-i;j++)
		{
			if(str[j] > str[j+1])
			{
				int tmp = str[j];
				str[j] = str[j+1];
				str[j+1] = tmp;
				flg = 1;
			}
		}
		if(!flg)
		{
			break;
		}
	}
}

六、快速排序hoare版本

int QuickSortChild1(int* arr,int begin,int end)
{
  int key = begin;
  while(begin < end)
  {
    while(begin < end && arr[end] >= arr[key])
    {
      end--;
    }
    while(begin < end && arr[begin] <= arr[key])
    {
      begin++;
    }

    Swap(&arr[end],&arr[begin]);
  }
  Swap(&arr[key],&arr[begin]); 
  return begin;
}

//快排
void QuickSort(int* arr,int begin,int end)
{
  //注意递归出口
  if(begin < end)
  {
    int mid = QuickSortChild1(arr,begin,end);
    QuickSort(arr,begin,mid - 1);
    QuickSort(arr,mid + 1,end);

  }
}

七、快速排序 挖坑法

int QuickSortChild2(int* arr,int begin,int end)
{
  int key =arr[begin];
  while(begin < end)
  {
    while(begin < end && arr[end] >= key)
    {
      end--;
    }
    arr[begin] = arr[end];

    while(begin < end && arr[begin] <= key)
    {
      begin++;
    }
    arr[end] = arr[begin];
  }
  arr[begin] = key;
  return begin;
}
void QuickSort(int* arr,int begin,int end)
{
  //注意递归出口
  if(begin < end)
  {
    int mid = QuickSortChild2(arr,begin,end);
    QuickSort(arr,begin,mid - 1);
    QuickSort(arr,mid + 1,end);

  }
}

八、快速排序前后指针版本

//双指针法
int QuickSortChild3(int* arr,int begin,int end)
{
  int pref = begin - 1;
  int cur = begin;
  int key = end;
  //定义pref,cur 。pref指向cur的前一个
  while(cur < end)
  {
    //cur去找比key小的元素,如果找到,就把pref++,就交换cur和pref下标所在的元素
    if(arr[cur] < arr[key])
    {
      pref++;
      Swap(&arr[cur],&arr[pref]);
    }
    //如果cur所在下标的元素比key所在元素大,就直接后移cur
    cur++;
  }
  //最后注意和end位置的元素交换的是pref的下一个,不是它自己
  Swap(&arr[++pref],&arr[end]);
  return pref;
}
void QuickSort(int* arr,int begin,int end)
{
  //注意递归出口
  if(begin < end)
  {
    int mid = QuickSortChild3(arr,begin,end);
    QuickSort(arr,begin,mid - 1);
    QuickSort(arr,mid + 1,end);

  }
}

在这里插入图片描述
注意:双指针法是用cur去找比key小的元素,如果找到就把pref++,交换Swap(&arr[cur],&arr[pref]);继续循环,最后注意不是拿pref和key进行交换,而是拿pref的下一个和key进行交换Swap(&arr[++pref],&arr[end]);

九、快速排序优化–三数取中

//快排优化--->三数取中,防止有序的情况
void medianOfThree(int* arr,int begin,int end)
{
  int mid = (begin + end) >> 1;
  if(arr[mid] > arr[begin])
  {
    Swap(&arr[mid],&arr[begin]);
  }
  if(arr[mid] > arr[end])
  {
    Swap(&arr[mid],&arr[end]);
  }
  if(arr[end] < arr[begin])
  {
    Swap(&arr[end],&arr[begin]);
  }
}
void QuickSort(int* arr,int begin,int end)
{
  //注意递归出口
  if(begin < end)
  {
  medianOfThree(arr,begin,end);
    int mid = QuickSortChild3(arr,begin,end);
    QuickSort(arr,begin,mid - 1);
    QuickSort(arr,mid + 1,end);

  }
}

十、快速排序----小区间优化

//快排优化--->小区间优化 + 三数取中
void QuickSort2(int* arr,int begin,int end)
{
  if(begin < end)
  {
  //小区间就直接使用选择排序,注意边界条件的控制
    if(end - begin + 1 < 10)
    {
      SelectSort(arr + begin,end - begin + 1);
    }
    else
    {

      medianOfThree(arr,begin,end);
      int mid = QuickSortChild3(arr,begin,end);
      QuickSort1(arr,begin,mid - 1);
      QuickSort1(arr,mid + 1,end);

    }
  }
}

十一、快速排序非递归版:

void QuickSort(int* arr,int left,int right)
{
  stack<int> s;
  s.push(left);
  s.push(right);

  while(!s.empty())
  {
    int end = s.top();
    s.pop();
    int begin = s.top();
    s.pop();

    int mid = QuickChild(arr,begin,end);
    //cout<<"i am quicksort"<<endl;
    if(begin < mid - 1)
    {
      s.push(begin);
      s.push(mid - 1);
    }

    if(mid + 1 < end)
    {
      s.push(mid + 1);
      s.push(end);
    }
  }
  for(int i = 0;i <= right;i++)
  {
    cout<<arr[i]<<" < ";
  }
  cout<<endl;
}

十二、归并排序:

//归并排序
void MergeSortChild(int* arr,int begin,int end,int* temp)
{
  if(begin == end)
  {
    return ;
  }
  int mid = (begin + end) >> 1;
  
  MergeSortChild(arr,begin,mid,temp);
  MergeSortChild(arr,mid + 1,end,temp);
  
  int begin1 = begin;
  int end1 = mid;
  int begin2 = mid + 1;
  int end2 = end;


  int index = begin;
  while(begin1 <= end1 && begin2 <= end2 )
  {
    if(arr[begin1] < arr[begin2])
    {
      temp[index] = arr[begin1];
      index++;
      begin1++;
    }
    else 
    {
      temp[index] = arr[begin2];
      index++;
      begin2++;
    }
  }
  while(begin1 <= end1)
  {
    temp[index] = arr[begin1];
    index++;
    begin1++;
  }
  while(begin2 <= end2)
  {
    temp[index] = arr[begin2];
    index++;
    begin2++;
  }

  memcpy(arr + begin,temp + begin,sizeof(int) * (end - begin + 1));
}

void MergeSort(int* arr,int n)
{
  int* temp = (int*)malloc(sizeof(int) * n);
  MergeSortChild(arr,0,n - 1,temp);
}


在这里插入图片描述
基本思想:

  • 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and
    Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有
    序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤

  • 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

  • 时间复杂度:O(N*logN)

  • 空间复杂度:O(N)

  • 稳定性:稳定

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页