C++学习笔记(35)

news/2024/9/29 2:25:49/

四十三、希尔排序
示例:
#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;
}
 


http://www.ppmy.cn/news/1531731.html

相关文章

【Redis 源码】1下载与源码编译

1 下载地址 GitHub - redis/redis-hashes: Redis tarball SHA1 hashes 本次下载的是6.2.5 版本 2 编译 在 redis目录下执行make make CFLAGS"-g -O0"“-O0” 参数表示告诉编译器不要优化代码&#xff0c;防止你在 Debug 的时候&#xff0c; IDE 里面的 Redis 源码…

R包:ggheatmap热图

加载R包 # devtools::install_github("XiaoLuo-boy/ggheatmap")library(ggheatmap) library(tidyr)数据 set.seed(123) df <- matrix(runif(225,0,10),ncol 15) colnames(df) <- paste("sample",1:15,sep "") rownames(df) <- sapp…

给Ubuntu虚拟机设置静态IP地址(固定IP)

查看 为Ubuntu虚拟机配置静态IP地址&#xff08;固定IP&#xff09;的方法经过亲自测试&#xff0c;已被证实有效。 这里你记得网关就可以了&#xff0c;等下要用 查看配置前的网络信息 ifconfig 查看网关 route -n 配置 配置网络文件 cd /etc/netplan/ ls 查看自己的文件的名…

Linux Mint急救模式

在给Linux MInt22安装6.8.0-45-generic的时候&#xff0c;出现一些问题&#xff0c;无法安装&#xff0c;手工单独安装后&#xff0c;发现无法启动。使用急救模式修复。 1 官方网站下载Linux Mint22的ISO镜像 2 下载Rufus刻盘工具 3 使用Rufus将ISO镜像刻到USB设备 4 使用U…

数据结构 随机存取 顺序存取 随机存储 顺序存储

存取结构&#xff1a;分为随机存取和非随机存取&#xff08;又称顺序存取&#xff09; 1、随机存取 就是直接存取&#xff0c;可以通过下标直接访问的那种数据结构&#xff0c;与存储位置无关&#xff0c;例如数组。 非随机存取就是顺序存取了&#xff0c;不能通过下标访问了…

c语言200例 64

大家好&#xff0c;欢迎来到无限大的频道。 今天带领大家来学习c语言。 题目要求&#xff1a; 设计一个进行候选人的选票程序。假设有三位候选人&#xff0c;在屏幕上输入要选择的候选人姓名&#xff0c; 有10次投票机会&#xff0c;最后输出每个人的得票结果。好的&#xff…

react组件入门

react应用程序就是由一个个组件搭建而成。组件有类组件和函数组件两种。 我们之前使用create-react-app创建了app&#xff0c;src下放的就是我们应用的源代码&#xff0c;我们基于这些已生成的文件&#xff0c;来学习和验证组件。 类组件 这里我们创建PostList.js更改这个ap…

程序员数学 | 数学归纳法

目录 一、数学归纳法是什么二、使用编程来模拟数学归纳法的证明 人类做重复性的劳动没有效率&#xff0c;而计算机却能更快更准确的完成重复性劳动。所以以重复为特点的迭代法在编程中有着⼴泛的应⽤。实际项目中是否可以用不断更新变量值或者缩小搜索的区间范围的方法&#xf…