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;
}
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/145967.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

BUUCTF-MISC-隐藏的钥匙

下载题目文件&#xff0c;获得了一张格式为jpg的路飞图片 按照习惯&#xff0c;首先使用十六进制编译器打开文件&#xff0c;这里我使用winhex打开文件 首先考虑有没有flag直接隐写在文件中&#xff0c;按照图示步骤查找flag字段 我们查到了flag&#xff0c;通过经验和图中base…

MySQL的缓存策略

目录 一、MySQL 缓存方案用来干什么 二、提升MySQL访问性能的方式 1、读写分离&#xff08;MySQL的主从复制&#xff09; 2、连接池 3、异步连接 三、缓存方案是怎么解决的 1、缓存与MySQL一致性状态分析 2、制定热点数据的读写策略 四、缓存方案问题的解决方法 1、缓…

正点原子阿尔法ARM开发板-IMX6ULL(八)——串口通信(寄存器解释)(补:有源蜂鸣器)

文章目录 一、蜂鸣器&#xff08;待&#xff0c;理解&#xff09;1.1 第一行1.2 第二行1.3 第三行 二、串口原理2.1 通信格式2.2 UART寄存器 一、蜂鸣器&#xff08;待&#xff0c;理解&#xff09; 1.1 第一行 对于第一行&#xff0c;首先先到fsl_iomuxc文件里面寻找IOMUXC_S…

人力资源数据集分析(一)_t-test、卡方检验和描述性统计

数据入口&#xff1a;人力资源分析数据集 - Heywhale.com 数据说明 字段说明EmpID唯一的员工IDAge年龄AgeGroup年龄组Attrition是否离职BusinessTravel出差&#xff1a;很少、频繁、不出差DailyRate日薪Department任职部门&#xff1a;研发部门、销售部门、人力资源部门Dista…

【VUE3.0】如何得到一张像素风格的图片?

目录 引言网络途径获取代码转换已有的图片0. 先看效果1. 上传图片&#xff0c;这个没什么好说的&#xff0c;前端上传图片基本操作。2. 通过滑动条提供一个1-10的数字&#xff0c;用于放缩图片画质。3. 函数拿到图片资源后先对图片进行缩小100倍尺寸处理&#xff0c;此时画质已…

服务器非法关闭后MySQL服务启动失败

在写这篇文章前&#xff0c;我弄好了&#xff0c;写完之后把成功安装的几个MySQL都删除了&#xff0c;只留了最后测试成功的服务“mysql-test” ,然后点击运行&#xff0c;发现又出现上图的错误。心态炸了。 本以为定位到问题了&#xff0c;但是这个错误让我迷茫了。我只能临时…

为什么你的广告规模无法扩大

许多跑facebook的广告主可能都遇到过这样的情况&#xff0c;小额测试广告的时候效果不错&#xff0c;一旦加预算想扩大规模广告往往就会崩掉&#xff0c;始终无法把广告提升一个level,如果你尝试了很多投放策略调整都无法挽救的话&#xff0c;可能问题是出在广告素材上。 对于一…

多重指针变量(n重指针变量)实例分析

0 前言 指针之于C语言&#xff0c;就像子弹于枪械。没了子弹的枪械虽然可以用来肉搏&#xff0c;却失去了迅速解决、优雅解决战斗的能力。但上了膛的枪械也非常危险&#xff0c;时刻要注意是否上了保险&#xff0c;使用C语言的指针也是如此&#xff0c;要万分小心&#xff0c;…

杀死端口占用的进程

1、查看端口的进程&#xff0c;以9023为例 &#xff08;1&#xff09;方法1 netstat -tunpl|grep 9023 &#xff08;2&#xff09;方法2 ss -tulpan |grep 9023 &#xff08;3&#xff09;方法3 netstat -ntlp |grep 9023 &#xff08;4&#xff09;方法4 lsof -i:9023 …

A Simple Encoder-Decoder for Open-Vocabulary Semantic Segmentation

FAM: Feature Aggregation Module&#xff0c;Circle with R represents removing feature maps of non-selected categories 辅助信息 权重有1.3G&#xff0c;不建议复现

变压器空载时是否有必要做无功补偿

在电力系统中&#xff0c;变压器作为关键设备之一&#xff0c;其运行状态对整个系统的功率质量和效率具有重要影响。关于“变压器空载时是否有必要做无功补偿”这一问题&#xff0c;答案取决于具体的应用场景、系统需求以及经济性考虑。以下将从变压器空载特性、无功补偿的原理…

360手机黑科技“位置穿越”功能修复 360位置穿越使用

​ 360手机刷机 360手机黑科技 360手机位置穿越 360手机位置修复 360手机站&#xff1a;360os.top 资源免费下载: os.360os.top 备用资源站&#xff1a;360手机-360手机刷机RootTwrp 360手机位置穿越 360手机位置穿越‌&#xff0c;是一款虚拟定位软件&#xff0c;无需进行r…

毕业设计选题:基于springboot+vue+uniapp的驾校报名小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

块匹配算法简介(上)

图像中的运动估计方法大致分为两类:光流法和块匹配算法(BMA,Block Matching Algorithm)。本文将介绍BMA的相关内容,包括基本原理、相似度计算准则与常见的几种搜索方法,如三步法、四步法、钻石搜索法等。 1. 背景 视频中相邻帧往往存在大量的相似内容,即只有局部的一些…

算法课习题汇总(2)

整数划分问题 将正整数n表示成一系列正整数之和&#xff0c;nn1n2…nk(n1>n2>…>nk,k>1)。正整数n的这种表示称为正整数n的划分。 思路&#xff1a; n表示待划分数&#xff0c;m表示最大减数。 #include<iostream> using namespace std;int q(int n, int…

JIT(即时编译)技术

介绍一下JIT优化技术&#xff1f; 想要把高级语言转变成计算机认识的机器语言有两种方式&#xff0c;分别是编译和解释&#xff0c;虽然Java转成机器语言的过程中有一个步骤是要编译成字节码&#xff0c;但是&#xff0c;这里的字节码并不能在机器上直接执行。 JVM中内置了 解释…

记软件开发者画图(UML),使用WPS应用制图

目录 前言 一、什么是UML 二、使用什么画图工具 三、示例 ​四、IntelliJ IDEA 2021快速生成UML图 前言 做软件开发的从写第一个示例程序到最后写项目程序避不开的需要设计画图&#xff0c;所以今天我们就来梳理一下‌UML&#xff08;统一建模语言&#xff09;图形需要画…

LINUX网络编程:TCP(1)

目录 1.认识Tcp的报头 2.确认应答机制&#xff08;ACK&#xff09; 序号与确认序号 捎带应答 3.超时重传机制 4.Tcp连接管理 三次握手 为什是三次握手 四次挥手 理解TIMEWAIT 1.认识Tcp的报头 源端口和目的端口号没什么说的 32位的序号和确认序号&#xff0c;之后会介…

T9-猫狗识别2(暂时版qaq)

T9周&#xff1a;猫狗识别2 **一、前期工作**1.设置GPU,导入库2.导入数据3.查看数据 **二、数据预处理**1.加载数据2.可视化数据3.配置数据集 **三、构建CNN网络模型****四、编译模型****五、训练模型****六、模型评估****七、预测**八、总结&#xff08;暂时&#xff09; &…

倒排索引(反向索引)

倒排索引&#xff08;Inverted Index&#xff09;是搜索引擎和数据库管理系统中常用的一种数据结构&#xff0c;用于快速检索文档集合中的文档。在全文搜索场景中&#xff0c;倒排索引是一种非常高效的手段&#xff0c;因为它能够快速定位到包含特定关键词的所有文档。 1、基本…