排序详解

排序详解

一、选择排序

代码:

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 quickSort3(int *str, int len)
{
	int stack[MaxSize * 2];
	int top = 0;
	int low = 0;
	int hight = len - 1;
	int mid = QuickShortchild(str, low, hight);//先进行一趟快排,得到一个分界点,
	//判断mid左右两边是否两个数据以上,如果有,分别把左右两边元素下标入栈
	if (mid > low + 1)
	{
		stack[top++] = low;
		stack[top++] = mid - 1;
	}
	if (mid < hight - 1)
	{
		stack[top++] = mid + 1;
		stack[top++] = hight;
	}
	//以上代码执行完毕,说明下标入栈完毕
	//判断栈是否为空,如果不为空,取出两个下标赋给hight,low,后进行循环,直至栈为空。
	while (top > 0)
	{
		hight = stack[--top];
		low = stack[--top];

		mid = QuickShortchild(str, low, hight);
		if (mid > low + 1)
		{
			stack[top++] = low;
			stack[top++] = mid - 1;
		}
		if (mid < hight - 1)
		{
			stack[top++] = mid + 1;
			stack[top++] = hight;
		}
	}
}

十二、归并排序:

//归并排序
void mergeChild(int *str,int start,int mid,int end)//把一个一个的序列排序并合并在一起
{
	int tempstr[MaxSize];
	int tempIndex = start;
	int start2 = mid + 1;
	int i = start;
	while (start <= mid && start2 <= end)//两个归并段都有数据的时候
	{
		if (str[start] <= str[start2])
		{
			tempstr[tempIndex] = str[start];
			start++;
			tempIndex++;
		}
		else
		{
			tempstr[tempIndex] = str[start2];
			start2++;
			tempIndex++;
		}
	}
	while (start <= mid)
	{
		tempstr[tempIndex] = str[start];
		start++;
		tempIndex++;
	}
	while (start2 <=  end)
	{
		tempstr[tempIndex] = str[start2];
		start2++;
		tempIndex++;
	}
	while (i <= end)//把每次合并排序完成的元素拷贝到原数组中
	{
		str[i] = tempstr[i];
		i++;
	}
}
void mergeSort(int *str, int start,int end)
{
	if (start >= end)
	{
		return;
	}
	int mid = (start + end) / 2;//该过程是把数组分解为一个一个的序列
	mergeSort(str,start, mid);
	mergeSort(str, mid+1 , end);

	mergeChild(str,start,mid ,end);//合并和排序过程
}

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

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

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

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

  • 空间复杂度:O(N)

  • 稳定性:稳定

发布了49 篇原创文章 · 获赞 15 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/wolfGuiDao/article/details/103917306