C++学习笔记(35)

四十三、希尔排序

示例:

#include <iostream>

using namespace std;

// 对希尔排序中的单个组进行排序。

// arr-待排序的数组,len-数组总的长度,num-小组的编号(从 0 开始),step-分组的步长(增量)。

void groupsort(int arr[], int len, int num, int step)

{

int itmp; // 当前需要排序的元素的值。

for (int ii = num + step; ii < len; ii = ii + step) // 每个小组的第一个元素不需要排序,就像

拿到第一张牌一样。

{

itmp = arr[ii]; // 待排序元素

// 从已排序的最右边开始,把大于当前排序的元素后移。

int jj; // 需要后移元素的计数器。

for (jj = ii - step; jj >= 0; jj = jj - step)

{

if (arr[jj] <= itmp) break;

arr[jj + step] = arr[jj]; // 逐个元素后移。

}

arr[jj + step] = itmp; // 插入当前排序元素。

}

}

// 希尔排序,arr 是待排序数组的首地址,len 是数组的大小。

void shellsort(int arr[], unsigned int len)

{

// step 为步长,每次减为原来的一半取整数,最后一次必定为 1。

for (int step = len / 2; step > 0; step = step / 2)

{

// 共 step 个组,对每一组都执行插入排序。

for (int ii = 0; ii < step; ii++)

{

groupsort(arr, len, ii, step);

}

}

}

int main(int argc, char* argv[])

{

int arr[] = { 44,3,38,5,47,15,36,26,27,2,46,4,19,50,48 }; // 待排序的数组。

int len = sizeof(arr) / sizeof(int); // 求数组长度。

shellsort(arr, len); // 希尔排序。

// 如果第三个参数填 0,第四个参数填 1,效果等同普通插入排序。

// groupsort(arr, len,0,1);

// 显示排序结果。

for (int ii = 0; ii < len; ii++) cout << arr[ii] << " ";

cout << endl;

}

四十四、快速排序

示例:

#include <iostream>

using namespace std;

void quicksort(int arr[], int len)

{

if (len < 2) return; // 数组的元素小于 2 个就不用排序了。

int mid = arr[0]; // 选取最左边的元素作为中心轴(基准数)。

int left = 0; // 左下标。

int right = len - 1; // 右下标。

int moving = 2; // 当前应该移动的下标,1-左下标;2-右下标,缺省取值 2。

while (left < right)

{

if (moving == 1) // 移动左下标的情况。

{

// 如果左下标位置元素的值小等于中心轴,继续移动左下标。

if (arr[left] <= mid) { left++; continue; }

arr[right] = arr[left]; // 如果左下标位置元素的值大于中心轴,把它填到右下标

的坑中。

right--; // 右下标向左移动。

moving = 2; // 下次循环将移动右下标。

}

else // 移动右下标的情况。

{

// 如果右下标位置元素的值大于等于中心轴,继续移动右下标。

if (arr[right] >= mid) { right--; continue; }

arr[left] = arr[right]; // 如果右下标位置元素的值小于中心轴,把它填到左下标

的坑中。

left++; // 左下标向右移动。

moving = 1; // 下次循环将移动左下标。

}

}

// 如果循环结束,左右下标重合,把中心轴的值填进去。

arr[left] = mid;

quicksort(arr, left); // 对中心轴左边的序列进行排序。

quicksort(arr + left + 1, len - left - 1); // 对中心轴右边的序列进行排序。

}

int main(int argc, char* argv[])

{

int arr[] = { 44,3,38,5,47,15,36,26,27,2,46,4,19,50,48 }; // 待排序的数组。

int len = sizeof(arr) / sizeof(int); // 求数组长度。

quicksort(arr, len);

// 显示排序结果。

for (int ii = 0; ii < len; ii++) cout << arr[ii] << " ";

cout << endl;

}

四十五、计数排序

示例:

#include <iostream>

using namespace std;

// 获取待排序数组元素中的最小和最大值。

void arrminmax(int arr[], int len, int& minvalue, int& maxvalue)

{

int ii = 0;

minvalue = maxvalue = arr[0];

for (ii = 0; ii < len; ii++)

{

if (maxvalue < arr[ii]) maxvalue = arr[ii];

if (minvalue > arr[ii]) minvalue = arr[ii];

}

}

// 计数排序主函数,arr-待排序数组的地址,len-数组的长度。

void countsort(int arr[], int len)

{

if (len < 2) return;

// 获取待排序数组元素中的最小和最大值。

int minvalue, maxvalue;

arrminmax(arr, len, minvalue, maxvalue);

// 分配并初始化临时数组。

int* arrtmp = new int[maxvalue - minvalue + 1];

memset(arrtmp, 0, sizeof(int)*(maxvalue - minvalue + 1));

// 临时数组计数。

for (int ii = 0; ii < len; ii++) arrtmp[arr[ii] - minvalue]++;

// 把临时数组计数的内容恢复到到 arr 数组中。

for (int ii=0,jj = 0; jj < maxvalue - minvalue + 1; jj++)

{

for (int kk = 0; kk < arrtmp[jj]; kk++) arr[ii++] = jj + minvalue;

}

delete[] arrtmp; // 释放临时数组。

}

int main(int argc, char* argv[])

{

int arr[] = { 2,3,8,7,1,2,2,2,7,3,9,8,2,1,4,2,4,6,9,2 }; // 待排序的数组。

int len = sizeof(arr) / sizeof(int); // 求数组长度。

countsort(arr, len);

// 显示排序结果。

for (int ii = 0; ii < len; ii++) cout << arr[ii] << " ";

cout << endl;

}

四十六、桶排序

示例(不用 InitList()分配头结点):

#include <iostream>

using namespace std;

typedef int ElemType; // 自定义链表的数据元素为整数。

struct LNode // 单链表的结点。

{

ElemType data; // 存放结点的数据元素。

struct LNode* next; // 指向下一个结点的指针。

};

// 初始化链表,返回值:失败返回 nullptr,成功返回头结点的地址。

LNode* InitList()

{

LNode* head = new (std::nothrow) LNode; // 分配头结点。

if (head == nullptr) return nullptr; // 内存不足,返回失败。

head->next = nullptr; // 头结点的下一结点暂时不存在,置空。

return head; // 返回头结点。

}

// 销毁链表。

void DestroyList(LNode* head)

{

// 销毁链表是指释放链表全部的结点,包括头结点。

LNode* tmp;

while (head != nullptr)

{

tmp = head->next; // tmp 保存下一结点的地址。

delete head; // 释放当前结点。

head = tmp; // 指针移动到下一结点。

}

}

// 在链表的头部插入元素(头插法),返回值:false-失败;true-成功。

bool PushFront(LNode* head, const ElemType& ee)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

LNode* tmp = new (std::nothrow) LNode; // 分配一个新结点。

if (tmp == nullptr) return false;

tmp->data = ee; // 把元素的值存入新结点。

// 处理 next 指针。

tmp->next = head->next;

head->next = tmp;

return true;

}

// 显示链表中全部的元素。

void PrintList(const LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return; }

LNode* pp = head->next; // 从第 1 个结点开始。

while (pp != nullptr)

{

cout << pp->data << " "; // 如果元素为结构体,这行代码要修改。

pp = pp->next; // 指针往后移动一个结点。

}

cout << endl;

}

// 在链表的尾部插入元素(尾插法),返回值:false-失败;true-成功。

bool PushBack(LNode* head, const ElemType& ee)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

LNode* pp = head; // 从头结点开始。

// 找到最后一个结点,pp 将指向尾结点(最后一个结点)。

while (pp->next != nullptr) pp = pp->next;

LNode* tmp = new (std::nothrow) LNode; // 分配一个新结点。

if (tmp == nullptr) return false;

tmp->data = ee; // 把元素的值存入新结点。

// 处理 next 指针。

tmp->next = nullptr;

pp->next = tmp;

return true;

}

// 求链表的表长,返回值:>=0-表 LL 结点的个数。

size_t ListLength(LNode* head)

{

//if (head == nullptr) { cout << "链表不存在。\n"; return 0; }

//LNode* pp = head->next; // 头结点不算,从第 1 个结点开始。

//size_t length = 0;

//while (pp != nullptr) { pp = pp->next; length++; }

//return length;

// 不使用临时变量,如何计算链表(包括头结点)的长度?

if (head==nullptr) return 0;

return ListLength(head->next)+1;

}

// 删除链表第一个结点。

bool PopFront(LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

if (head->next == nullptr) { cout << "链表为空,没有结点。\n"; return false; }

LNode* pp = head->next; // pp 指向第一个节点。

head->next = head->next->next; // 修改头结点的 next 指针。

delete pp; // 删除第一个节点。

return true;

}

// 删除链表最后一个结点。

bool PopBack(LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

// 必须加上这个判断,否则下面的循环 pp->next->next 不成立。

if (head->next == nullptr) { cout << "链表为空,没有结点。\n"; return false; }

LNode* pp = head; // 从头结点开始。

// 找到倒数第二个结点(包括头结点)。

while (pp->next->next != nullptr) pp = pp->next;

delete pp->next; // 释放最后一个结点。

pp->next = nullptr; // 倒数第二个结点成了最后一个结点。

return true;

}

// 清空链表,清空链表是指释放链表全部的结点,但是,不释放头结点。

void ClearList(LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return; } // 判断链表是否存在。

LNode* tmp1;

LNode* tmp2 = head->next; // 从头结点的下一个结点开始释放。

while (tmp2 != nullptr)

{

tmp1 = tmp2->next;

delete tmp2;

tmp2 = tmp1;

}

head->next = nullptr; // 这行代码一定不能少,否则会留下野指针。

}

// 查找元素 ee 在链表中的结点地址,如果没找到返回 nullptr,否则返回结点的地址。

LNode* LocateElem(const LNode* head, const ElemType& ee)

{

LNode* pp = head->next; // 从第 1 个存放数据结点开始。

while (pp != nullptr)

{

// 如果数据元素是结构体,以下代码要修改成比较关键字。

if (pp->data == ee) return pp;

pp = pp->next;

}

return pp;

}

// 获取链表中第 n 个结点,成功返回结点的地址,失败返回 nullptr。

// 注意,n 可以取值为 0,表示头结点。

LNode* LocateNode(LNode* head, unsigned int n)

{

if (head == nullptr) { cout << "链表不存在。\n"; return nullptr; }

LNode* pp = head; // 指针 pp 指向头结点,逐步往后移动,如果为空,表示后面没结

点了。

unsigned int ii = 0; // ii 指向的是第几个结点,从头结点 0 开始,pp 每向后移动一次,

ii 就加 1。

while ((pp != nullptr) && (ii < n))

{

pp = pp->next; ii++;

}

if (pp == nullptr) { cout << "位置" << n << "不合法,超过了表长。\n"; return nullptr; }

return pp;

}

// 在指定结点 pp 之后插入元素 ee。

bool InsertNextNode(LNode* pp, const ElemType& ee)

{

if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }

LNode* tmp = new LNode;

tmp->data = ee;

tmp->next = pp->next;

pp->next = tmp;

return true;

}

// 在指定结点 pp 之前插入元素 ee。

bool InsertPriorNode(LNode* pp, const ElemType& ee)

{

if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }

// 在指定结点 pp 之前插入采用偷梁换柱的方法:

// 1、分配一个新的结点;

// 2、把 pp 结点的数据和指针复制到新结点中。

// 3、把待插入元素的数据存入 pp 结点中。

LNode* tmp = new LNode;

// 把 pp 结点的数据和指针复制到 tmp 中。

tmp->data = pp->data;

tmp->next = pp->next;

// 把待插入的元素存入 pp 中。

pp->data = ee;

pp->next = tmp;

return true;

}

// 删除指定结点。

bool DeleteNode1(LNode* pp)

{

if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }

if (pp->next != nullptr) // 如果结点 pp 后面还有结点。

{

// 删除指定结点的思想是:1)把 pp 后继结点的数据和 next 指针复制到 pp 结点;2)删

除 pp 结点的后继结点。

LNode* tmp = pp->next; // tmp 指向 pp 的后继结点。

pp->data = tmp->data; // 把后继结点的数据复制到 pp 结点中。

pp->next = tmp->next; // 把 pp 的 next 指向后继结点的 next。

delete tmp; // 释放后继结点。

return true;

}

else // 如果结点 pp 是最后一个结点。

{

return false; // 可以在函数外面调用 PopBack()函数。

}

}

// 把元素有序的插入链表中,返回值:false-失败;true-成功。

bool PushOrder(LNode* head, const ElemType& ee)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

LNode* tail = head; // 将指向尾结点。

LNode* pp = head->next; // 从第一个结点开始。

while (pp != nullptr)

{

// 如果结点的元素大于 ee,把 ee 插入该结点前面。

if (pp->data > ee) { InsertPriorNode(pp, ee); return true; }

tail = pp;

pp = pp->next; // 指针后移一个结点。

}

// 如果在上面的循环中没能插入元素,表示 ee 应该插入到链表的尾部。

if (pp == nullptr) return InsertNextNode(tail, ee);

return true;

}

// 采用归并的方法,将两个升序的链表 La 和 Lb,合并成一个升序的链表 Lc。

// 合并后,链表 La 和 Lb 将不再拥有结点(只有头结点)。

void MergeList(LNode *La, LNode *Lb, LNode *Lc)

{

LNode* pa = La->next;

LNode* pb = Lb->next;

LNode* pp;

// 把 pa 和 pb 合并到 Lc 中。

while ((pa != nullptr) && (pb != nullptr))

{

// 取 pa 和 pb 的较小者。

if (pa->data <= pb->data)

{

pp = pa; pa = pa->next;

}

else

{

pp = pb; pb = pb->next;

}

// 把较小的结点 pp 追加到 Lc 中。

Lc->next = pp;

Lc = Lc->next;

}

// 把链表 pa 其它的元素追加到 Lc 中。

if (pa != nullptr) Lc->next = pa;

// 把链表 pb 其它的元素追加到 Lc 中。

if (pb != nullptr) Lc->next = pb;

// 链表 La 和 Lb 的结点已全部转移给了 Lc,需置空,否则 DestroyList()时会引起重复释放结点。

La->next = Lb->next = nullptr;

}

// 桶排序主函数,参数 arr 是待排序数组的首地址,len 是数组元素的个数,bucketnum 是桶的个

数。

void bucketsort(int arr[], int len, int bucketnum)

{

// 分配桶头结点的结构体数组。

LNode* buckets = new LNode[bucketnum];

// 初始化桶,把头结点的 next 指针置为空。

for (int ii = 0; ii < bucketnum; ii++)

buckets[ii].next = nullptr;

// 把数组 arr 全部的元素放入桶中。

for (int ii = 0; ii < len; ii++)

{

PushOrder(&buckets[arr[ii] % bucketnum] ,arr[ii]);

}

// 显示每个桶中的元素。

cout << "每个桶中的元素如下:\n";

for (int ii = 0; ii < bucketnum; ii++) PrintList(&buckets[ii]);

// 把全部桶中的元素归并到 LL 中。

LNode LL; LL.next = nullptr;

LNode tmp; tmp.next = nullptr;

for (int ii = 0; ii < bucketnum; ii++)

{

MergeList(&buckets[ii], &LL, &tmp); // 把桶中的链表和 LL 合并到 tmp 中。

swap(LL.next, tmp.next); // 交换 LL 和 tmp 的数据结点。

}

cout << "显示合并后的结果:\n"; PrintList(&LL);

// 把合并后的结果保存到数组 arr 中。

LNode* pp = LL.next;

for (int ii = 0; ii < len; ii++)

{

arr[ii] = pp->data; pp = pp->next;

}

DestroyList(LL.next); // 释放链表 LL 的数据结点。

delete[] buckets; // 释放桶的结构体数组(头结点们)。

}

int main()

{

// 链表语义是动态分配内存,包括头节点,也是动态分配出来的。

// 以下代码用于测试 PushOrder()函数的功能。

//LNode LL; LL.next = nullptr; // = InitList(); // 初始化链表 LL。

//cout << "有序的插入元素(5、8、7、4、1、3、9、3、6)。\n";

//PushOrder(&LL, 5);

//PushOrder(&LL, 8);

//PushOrder(&LL, 7);

//PushOrder(&LL, 4);

//PushOrder(&LL, 1);

//PushOrder(&LL, 3);

//PushOrder(&LL, 9);

//PushOrder(&LL, 3);

//PushOrder(&LL, 6);

//PrintList(&LL); // 把链表中全部的元素显示出来。

//DestroyList(LL.next); // 销毁链表 LL 的数据结点。

// 以下代码用于测试 MergeList()函数的功能。

//LNode La; La.next = nullptr;

//LNode Lb; Lb.next = nullptr;

//LNode Lc; Lc.next = nullptr;

//PushOrder(&La, 5);

//PushOrder(&La, 8);

//PushOrder(&La, 7);

//PushOrder(&La, 4);

//PushOrder(&Lb, 1);

//PushOrder(&Lb, 3);

//PushOrder(&Lb, 9);

//PushOrder(&Lb, 2);

//PushOrder(&Lb, 6);

//MergeList(&La, &Lb, &Lc); // 把链表 La 和 Lb 合并成 Lc。

//PrintList(&Lc); // 把链表 Lc 中全部的元素显示出来。

DestroyList(La.next);

DestroyList(Lb.next);

//DestroyList(Lc.next);

//

以下代码用于测试桶排序的功能。

int arr[] = { 44,3,38,5,47,15,36,26,27,2,46,4,19,50,48 }; // 待排序的数组。

int len = sizeof(arr) / sizeof(int); // 求数组长度。

bucketsort(arr, len,3);

// 显示排序结果。

for (int ii = 0; ii < len; ii++) cout << arr[ii] << " ";

cout << endl;

}

示例(用 InitList()分配头结点):

#include <iostream>

using namespace std;

typedef int ElemType; // 自定义链表的数据元素为整数。

struct LNode // 单链表的结点。

{

ElemType data; // 存放结点的数据元素。

struct LNode* next; // 指向下一个结点的指针。

};

// 初始化链表,返回值:失败返回 nullptr,成功返回头结点的地址。

LNode* InitList()

{

LNode* head = new (std::nothrow) LNode; // 分配头结点。

if (head == nullptr) return nullptr; // 内存不足,返回失败。

head->next = nullptr; // 头结点的下一结点暂时不存在,置空。

return head; // 返回头结点。

}

// 销毁链表。

void DestroyList(LNode* head)

{

// 销毁链表是指释放链表全部的结点,包括头结点。

LNode* tmp;

while (head != nullptr)

{

tmp = head->next; // tmp 保存下一结点的地址。

delete head; // 释放当前结点。

head = tmp; // 指针移动到下一结点。

}

}

// 在链表的头部插入元素(头插法),返回值:false-失败;true-成功。

bool PushFront(LNode* head, const ElemType& ee)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

LNode* tmp = new (std::nothrow) LNode; // 分配一个新结点。

if (tmp == nullptr) return false;

tmp->data = ee; // 把元素的值存入新结点。

// 处理 next 指针。

tmp->next = head->next;

head->next = tmp;

return true;

}

// 显示链表中全部的元素。

void PrintList(const LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return; }

LNode* pp = head->next; // 从第 1 个结点开始。

while (pp != nullptr)

{

cout << pp->data << " "; // 如果元素为结构体,这行代码要修改。

pp = pp->next; // 指针往后移动一个结点。

}

cout << endl;

}

// 在链表的尾部插入元素(尾插法),返回值:false-失败;true-成功。

bool PushBack(LNode* head, const ElemType& ee)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

LNode* pp = head; // 从头结点开始。

// 找到最后一个结点,pp 将指向尾结点(最后一个结点)。

while (pp->next != nullptr) pp = pp->next;

LNode* tmp = new (std::nothrow) LNode; // 分配一个新结点。

if (tmp == nullptr) return false;

tmp->data = ee; // 把元素的值存入新结点。

// 处理 next 指针。

tmp->next = nullptr;

pp->next = tmp;

return true;

}

// 求链表的表长,返回值:>=0-表 LL 结点的个数。

size_t ListLength(LNode* head)

{

//if (head == nullptr) { cout << "链表不存在。\n"; return 0; }

//LNode* pp = head->next; // 头结点不算,从第 1 个结点开始。

//size_t length = 0;

//while (pp != nullptr) { pp = pp->next; length++; }

//return length;

// 不使用临时变量,如何计算链表(包括头结点)的长度?

if (head==nullptr) return 0;

return ListLength(head->next)+1;

}

// 删除链表第一个结点。

bool PopFront(LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

if (head->next == nullptr) { cout << "链表为空,没有结点。\n"; return false; }

LNode* pp = head->next; // pp 指向第一个节点。

head->next = head->next->next; // 修改头结点的 next 指针。

delete pp; // 删除第一个节点。

return true;

}

// 删除链表最后一个结点。

bool PopBack(LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

// 必须加上这个判断,否则下面的循环 pp->next->next 不成立。

if (head->next == nullptr) { cout << "链表为空,没有结点。\n"; return false; }

LNode* pp = head; // 从头结点开始。

// 找到倒数第二个结点(包括头结点)。

while (pp->next->next != nullptr) pp = pp->next;

delete pp->next; // 释放最后一个结点。

pp->next = nullptr; // 倒数第二个结点成了最后一个结点。

return true;

}

// 清空链表,清空链表是指释放链表全部的结点,但是,不释放头结点。

void ClearList(LNode* head)

{

if (head == nullptr) { cout << "链表不存在。\n"; return; } // 判断链表是否存在。

LNode* tmp1;

LNode* tmp2 = head->next; // 从头结点的下一个结点开始释放。

while (tmp2 != nullptr)

{

tmp1 = tmp2->next;

delete tmp2;

tmp2 = tmp1;

}

head->next = nullptr; // 这行代码一定不能少,否则会留下野指针。

}

// 查找元素 ee 在链表中的结点地址,如果没找到返回 nullptr,否则返回结点的地址。

LNode* LocateElem(const LNode* head, const ElemType& ee)

{

LNode* pp = head->next; // 从第 1 个存放数据结点开始。

while (pp != nullptr)

{

// 如果数据元素是结构体,以下代码要修改成比较关键字。

if (pp->data == ee) return pp;

pp = pp->next;

}

return pp;

}

// 获取链表中第 n 个结点,成功返回结点的地址,失败返回 nullptr。

// 注意,n 可以取值为 0,表示头结点。

LNode* LocateNode(LNode* head, unsigned int n)

{

if (head == nullptr) { cout << "链表不存在。\n"; return nullptr; }

LNode* pp = head; // 指针 pp 指向头结点,逐步往后移动,如果为空,表示后面没结

点了。

unsigned int ii = 0; // ii 指向的是第几个结点,从头结点 0 开始,pp 每向后移动一次,

ii 就加 1。

while ((pp != nullptr) && (ii < n))

{

pp = pp->next; ii++;

}

if (pp == nullptr) { cout << "位置" << n << "不合法,超过了表长。\n"; return nullptr; }

return pp;

}

// 在指定结点 pp 之后插入元素 ee。

bool InsertNextNode(LNode* pp, const ElemType& ee)

{

if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }

LNode* tmp = new LNode;

tmp->data = ee;

tmp->next = pp->next;

pp->next = tmp;

return true;

}

// 在指定结点 pp 之前插入元素 ee。

bool InsertPriorNode(LNode* pp, const ElemType& ee)

{

if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }

// 在指定结点 pp 之前插入采用偷梁换柱的方法:

// 1、分配一个新的结点;

// 2、把 pp 结点的数据和指针复制到新结点中。

// 3、把待插入元素的数据存入 pp 结点中。

LNode* tmp = new LNode;

// 把 pp 结点的数据和指针复制到 tmp 中。

tmp->data = pp->data;

tmp->next = pp->next;

// 把待插入的元素存入 pp 中。

pp->data = ee;

pp->next = tmp;

return true;

}

// 删除指定结点。

bool DeleteNode1(LNode* pp)

{

if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }

if (pp->next != nullptr) // 如果结点 pp 后面还有结点。

{

// 删除指定结点的思想是:1)把 pp 后继结点的数据和 next 指针复制到 pp 结点;2)删

除 pp 结点的后继结点。

LNode* tmp = pp->next; // tmp 指向 pp 的后继结点。

pp->data = tmp->data; // 把后继结点的数据复制到 pp 结点中。

pp->next = tmp->next; // 把 pp 的 next 指向后继结点的 next。

delete tmp; // 释放后继结点。

return true;

}

else // 如果结点 pp 是最后一个结点。

{

return false; // 可以在函数外面调用 PopBack()函数。

}

}

// 把元素有序的插入链表中,返回值:false-失败;true-成功。

bool PushOrder(LNode* head, const ElemType& ee)

{

if (head == nullptr) { cout << "链表不存在。\n"; return false; }

LNode* tail = head; // 将指向尾结点。

LNode* pp = head->next; // 从第一个结点开始。

while (pp != nullptr)

{

// 如果结点的元素大于 ee,把 ee 插入该结点前面。

if (pp->data > ee) { InsertPriorNode(pp, ee); return true; }

tail = pp;

pp = pp->next; // 指针后移一个结点。

}

// 如果在上面的循环中没能插入元素,表示 ee 应该插入到链表的尾部。

if (pp == nullptr) return InsertNextNode(tail, ee);

return true;

}

// 采用归并的方法,将两个升序的链表 La 和 Lb,合并成一个升序的链表 Lc。

// 合并后,链表 La 和 Lb 将不再拥有结点(只有头结点)。

void MergeList(LNode *La, LNode *Lb, LNode *Lc)

{

LNode* pa = La->next;

LNode* pb = Lb->next;

LNode* pp;

// 把 pa 和 pb 合并到 Lc 中。

while ((pa != nullptr) && (pb != nullptr))

{

// 取 pa 和 pb 的较小者。

if (pa->data <= pb->data)

{

pp = pa; pa = pa->next;

}

else

{

pp = pb; pb = pb->next;

}

// 把较小的结点 pp 追加到 Lc 中。

Lc->next = pp;

Lc = Lc->next;

}

// 把链表 pa 其它的元素追加到 Lc 中。

if (pa != nullptr) Lc->next = pa;

// 把链表 pb 其它的元素追加到 Lc 中。

if (pb != nullptr) Lc->next = pb;

// 链表 La 和 Lb 的结点已全部转移给了 Lc,需置空,否则 DestroyList()时会引起重复释放结点。

La->next = Lb->next = nullptr;

}

// 桶排序主函数,参数 arr 是待排序数组的首地址,len 是数组元素的个数,bucketnum 是桶的个

数。

void bucketsort(int* arr, int len,int bucketnum)

{

// 分配桶的指针数组。

LNode** buckets = new LNode*[bucketnum];

// 初始化桶。

for (int ii = 0; ii < bucketnum; ii++)

buckets[ii] = InitList();

// 把数组 arr 的数据放入桶中。

for (int ii = 0; ii < len; ii++)

{

PushOrder(buckets[arr[ii] % bucketnum] ,arr[ii]);

}

// 显示每个桶中的元素。

cout << "每个桶中的元素如下:\n";

for (int ii = 0; ii < bucketnum; ii++) PrintList(buckets[ii]);

// 把全部桶中的元素归并到 LL 中。

LNode* LL= InitList();

LNode* tmp = InitList();

for (int ii = 0; ii < bucketnum; ii++)

{

MergeList(buckets[ii], LL, tmp); // 把桶中的链表和 LL 合并到 tmp 中。

swap(LL->next, tmp->next); // 交换 LL 和 tmp 的数据结点。

}

cout << "显示合并后的结果:\n"; PrintList(LL);

// 把合并后的结果保存到数组 arr 中。

LNode* pp = LL->next;

for (int ii = 0; ii < len; ii++)

{

arr[ii] = pp->data; pp = pp->next;

}

DestroyList(LL);

DestroyList(tmp);

// 释放桶。

for (int ii = 0; ii < bucketnum; ii++)

{

DestroyList(buckets[ii]);

}

delete[] buckets; // 释放桶的指针数组。

}

int main()

{

// 以下代码用于测试 PushOrder()函数的功能。

//LNode* LL = InitList(); // 初始化链表 LL。

//cout << "有序的插入元素(5、8、7、4、1、3、9、3、6)。\n";

//PushOrder(LL, 5);

//PushOrder(LL, 8);

//PushOrder(LL, 7);

//PushOrder(LL, 4);

//PushOrder(LL, 1);

//PushOrder(LL, 3);

//PushOrder(LL, 9);

//PushOrder(LL, 3);

//PushOrder(LL, 6);

//PrintList(LL); // 把链表中全部的元素显示出来。

//DestroyList(LL); // 销毁链表 LL。

// 以下代码用于测试 MergeList()函数的功能。

//LNode* La = InitList();

//LNode* Lb = InitList();

//LNode* Lc = InitList();

//PushOrder(La, 5);

//PushOrder(La, 8);

//PushOrder(La, 7);

//PushOrder(La, 4);

//PushOrder(Lb, 1);

//PushOrder(Lb, 3);

//PushOrder(Lb, 9);

//PushOrder(Lb, 2);

//PushOrder(Lb, 6);

//MergeList(La, Lb, Lc); // 把链表 La 和 Lb 合并成 Lc。

//PrintList(Lc); // 把链表 Lc 中全部的元素显示出来。

//DestroyList(La);

//DestroyList(Lb);

//DestroyList(Lc);

//

// 以下代码用于测试桶排序的功能。

int arr[] = { 44,3,38,5,47,15,36,26,27,2,46,4,19,50,48 }; // 待排序的数组。

int len = sizeof(arr) / sizeof(int); // 求数组长度。

bucketsort(arr, len,3);

// 显示排序结果。

for (int ii = 0; ii < len; ii++) cout << arr[ii] << " ";

cout << endl;

}

相关推荐
烦躁的大鼻嘎23 分钟前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
IU宝26 分钟前
C/C++内存管理
java·c语言·c++
熙曦Sakura27 分钟前
完全竞争市场
笔记
fhvyxyci27 分钟前
【C++之STL】摸清 string 的模拟实现(下)
开发语言·c++·string
C++忠实粉丝40 分钟前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
古月居GYH1 小时前
在C++上实现反射用法
java·开发语言·c++
Betty’s Sweet1 小时前
[C++]:IO流
c++·文件·fstream·sstream·iostream
敲上瘾1 小时前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
不会写代码的ys1 小时前
【类与对象】--对象之舞,类之华章,共绘C++之美
c++
兵哥工控1 小时前
MFC工控项目实例三十二模拟量校正值添加修改删除
c++·mfc