数据结构学习笔记(一)
前言
参考:
1 绪论
思维导图
题目
1-1 最大子列和问题
/*01 最大子列和问题*/
#include <iostream>
using namespace std;
//算法1:
//时间复杂度 O(n^3)
int MaxSubseqsum1(int n, int a[]) {
int max = 0;
for (int i = 0; i < n; i++) { //控制子列尾部
for (int k = 0; k <= i; k++) { 控制子列头部
int tmpsum = 0;
for (int j = k; j <= i; j++) {
tmpsum = tmpsum + a[j];
}
if (max < tmpsum)
max = tmpsum;
}
}
cout << "MaxSubseqsum1求解的最大值是:" << max << endl;
return max;
}
//算法2:
//时间复杂度 O(n^2)
int MaxSubseqsum2(int n, int a[]) {
int max = 0;
for (int i = 0; i < n; i++) { //控制子列头部
int tmpsum = 0;
for (int k = i; k < n; k++) { //控制子列尾部,并求和
tmpsum += a[k];
if (max < tmpsum)
max = tmpsum;
}
}
cout << "MaxSubseqsum2求解的最大值是:" << max << endl;
return max;
}
//算法3:
//分治法
/*“分治法”,简单来说就是把一个大的问题分解成多个小问题求解,再从所有小的解里面寻求最优解。对于此问题而言,可以把一个大的序列分为两个小的序列,再把小的序列分为更小的两个序列,…,直到每个小序列只有一个数,这就是分的过程,在每个小序列中,会得到:
左边最大子列和(正数即本身,负数即0)
右边最大子列和
横跨划分边界的最大子列和
(比如对于只有 1 | 2 两个数的子列,其左边最大子列和为 1 ,右边最大子列和为 2,而横跨划分边界的最大子列和为 1+2)
此时三者中最大的值就是该小序列的"最大子列和",以此再得到更高层次的"最大子列和",…,最终得到整个问题的最大子列和*/
int Max3(int A, int B, int C) {
return A > B ? (A > C ? A : C) : (B > C ? B : C); //返回 ABC中的最大值
}
int DivideAndConquer(int a[],int left,int right)
{
//递归结束条件:子列只有一个数字
if (left == right) {
if (0 < a[left])
return a[left];
return 0;
}
//递归
int center = (left + right) / 2;
int MaxLeftSum = DivideAndConquer(a, left, center);
int MaxRightSum = DivideAndConquer(a, center + 1, right);
/*错误:求和时从左往右求和会导致,当中间部位(即边界部位)出现较大值的时候,无法求取*/
左右跨界最大子列和
左子列和
//int MaxLeftBorderSum = 0;
//int LeftBorderSum = 0;
//for (int i = left; i <= center; i++) {
// LeftBorderSum += a[i];
// if (MaxLeftBorderSum < LeftBorderSum)
// MaxLeftBorderSum = LeftBorderSum;
//}
右子列和
//int MaxRightBorderSum = 0;
//int RightBorderSum = 0;
//for (int i = center+1; i <= right; i++) {
// RightBorderSum += a[i];
// if (MaxRightBorderSum < RightBorderSum)
// MaxRightBorderSum = RightBorderSum;
//}
/* 再分别找左右跨界最大子列和*/
/*从边界向两边求和*/
int MaxLeftBorderSum = 0;
int LeftBorderSum = 0;
for (int i = center; i >= left; i--) { //应该从边界出发向左边找
LeftBorderSum += a[i];
if (MaxLeftBorderSum < LeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
int MaxRightBorderSum = 0;
int RightBorderSum = 0;
for (int i = center + 1; i <= right; i++) { // 从边界出发向右边找
RightBorderSum += a[i];
if (MaxRightBorderSum < RightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//返回 左边最大子列和、右边最大子列和、跨界最大子列和 中的最大值
Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubseqsum3(int n, int a[]) {
int max = DivideAndConquer(a, 0, n - 1);
cout << "MaxSubseqsum3求解的最大值是:" << max << endl;
return max;
}
//算法4:贪心法
/*“贪心法”,即不从整体最优上加以考虑,只做出某种意义上的局部最优解。其实最大子列和与它的首部和尾部都没有关系,我们只关心它当前的大小。当临时和加上当前值为负时,它对之后子列和肯定没有帮助(甚至只会让之后的和更小!),我们抛弃这段临时和将它置0*/
int MaxSubseqsum4(int n, int a[]) {
int max = 0;
int tmpsum = 0;
for (int i = 0; i < n; i++) {
tmpsum += a[i];
if (tmpsum < 0)tmpsum = 0;
else if (max < tmpsum)max = tmpsum;
}
cout << "MaxSubseqsum4求解的最大值是:" << max << endl;
return max;
}
int main() {
int n;
int a[10000];
cout << "请输入 n:" << endl;
cin >> n;
cout << "请输入 a[]:" << endl;
for (int i = 0; i < n; i++)
cin >> a[i];
MaxSubseqsum1(n, a);
MaxSubseqsum2(n, a);
MaxSubseqsum3(n, a);
MaxSubseqsum4(n, a);
return 0;
}
注:算法3分治法中,【再分别找左右跨界最大子列和】这一步需要从边界向两边求和,而不是从左向右。这是确保截断部位(边界)有较大值时可以求取。
1-2 最大子列和问题(提升)
分析
翻译过来就是求一个序列的最大子列和,并输出最大子列和的开头和结尾的数,求最大子列和我们用到了"贪心法",累加当前序列,如果序列和小于 0 就置 0 重新累加,否则更新最大值。
现在的问题是如何记录开头和结尾的数,结尾的数,就是每次更新最大值时记录当时的序列号,该序列号存的值就是结尾的数;而开头的数和每次序列和小于 0 相关,每次序列和清零表示抛弃了这段子序列,之后有新的开始,所以清 0 后下一个序列号里存的就是子序列开头的数,那么如何记录下这个数?对了,更新最大值的时候!每次更新最大值相当于固定一段子序列。
最后一个问题就是,当只有一个 0,其余全为负数的情况,按道理应该输出全 0,但是实际输出的是 “0 第一个数 最后一个数”,为了避免这种情况,我们将最大值初值赋为 -1,这样遇到 0 时也能更新最大值。
#include <iostream>
using namespace std;
//求和算法:贪心法
int MaxSubseqsum(int n, int a[]) {
int max = -1; //max设为负数,区别,所有子集为负 和 子集累加为 0
int tmpsum = 0;
int left, right;
int tmpleft;
for (int i = 0; i < n; i++) {
tmpsum += a[i];
if (tmpsum < 0) {
tmpsum = 0;
tmpleft = a[i+1]; //零时left值,记录每次tmpsum清零时tmpleft
}
else if (max < tmpsum) {
max = tmpsum;
right = a[i];
left = tmpleft; //当max更新时,记录对应的 left 和right
}
}
if (max >= 0) {
cout << "MaxSubseqsum求解的最大值是:" << max << endl;
cout << "开头结尾值分别为:" << left << " " << right << endl;
}
else { //max为负数,说明 所有子集为负数,没有进行累加操作
cout << "MaxSubseqsum求解的最大值是:" << 0 << endl;
cout << "开头结尾值分别为:" << "无" << endl;
}
return max;
}
int main() {
int n;
int a[10000];
cout << "请输入 n:" << endl;
cin >> n;
cout << "请输入 a[]:" << endl;
for (int i = 0; i < n; i++)
cin >> a[i];
MaxSubseqsum(n, a);
return 0;
}
注:初始max设为负数,用以区别,所有子集为负 和 子集累加为 0 两种情况
所有子集为负时,最大值为0。
1-3 二分查找
注:当 循环结束未找到,输出NotFound,找到则提前返回Mid
Position BinarySearch( List L, ElementType X )
{
//找到并返回
Position Pro =1;
Position Post = L->Last;
while(Pro <=Post)
{
Position Mid = (Pro + Post)/2;
if(L->Data[Mid] == X)return Mid;
else if (L->Data[Mid] < X){
Pro = Mid+1;
}
else if (L->Data[Mid] > X){
Post = Mid-1;
}
}
return NotFound;
}
如果while(Pro <Post)
不取=,则可能是最后一个数据刚好是要找的数,会错过。
如果Pro = Mid+1;
和Post = Mid-1;
没有+1 -1,则会运行超时。
2 线性结构
思维导图
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8ab3fdaf9ce7d97dd3d5a05206ea10ce.png#pic_center)
2.1什么是线性表
2.2线性表的抽象数据类型描述
代码实现
1. 线性表的顺序存储实现
利用数组的连续存储空间顺序存放线性表的各元素
注:顺序存储中是序号是下标,从 0 开始
#include<stdint.h>
#include<malloc.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define NoFound -1
#define ERROR -2
typedef int ElementType;
typedef struct LNode *List; //定义了一个指针,改指针指向的类型为struct LNode;
//Lnode x; 声明一个Lnode类型的变量x。结构体Node ,是一种类型。
//传值是L,传引用是&L,传指针是*L, 如果需要有原值的变更的话需要用传引用,或者传指针。
//LNode *s; 定义一个指向LNode的指针。
struct LNode
{
ElementType Data[MAXSIZE];
int last;
};
List L;
/*线性表基本操作*/
List MakeEmpty(); //初始化顺序表
int Find(ElementType X, List L); //查找 X 第一次出现的下标
void Insert(ElementType X, int i, List L); //在下标为 i 的地方插入 X
void Delete(int i, List L); //删除下标为 i 的当前值
ElementType FindKth(int K, List L); //返回下标为 K 的当前值
int Length(List L); //返回顺序表的长度
/*实现*/
List MakeEmpty() //初始化顺序表
{
List L;
L = (List)malloc(sizeof(struct LNode)); //为你的结点L动态分配内存,分配成功则返回指向被分配内存的指针,否则返回空指针NULL
//其中sizeof是取你定义的结构体取其总的字节数,即malloc后的括号内是要分内内存空间的大小
L->last = -1; //空表
return L;
}
//按值查找
int Find(ElementType X, List L)//查找 X 第一次出现的下标
{
int i = 0;
while (i <= L->last && X != L->Data[i])
i++;
if (i > L->last)
return -1;
else
return i;
}
void Insert(ElementType X, int i, List L) //在下标为 i 的地方插入 X
{
if (L->last == MAXSIZE - 1) {
cout << "表满" << endl;
return;
}
if (i<0 || i > L->last + 1) {
cout << "位置越界" << endl;
return;
}
//后移
for (int j = L->last; j >= i; j--) {
L->Data[j + 1] = L->Data[j];
}
//插入
L->Data[i] = X;
L->last++;
return;
}
void Delete(int i, List L) //删除下标为 i 的当前值
{
if (i<0 || i > L->last)
{
cout << "位置越界,不存在元素L->Date[" << i << "]" << endl;
return;
}
//前移
for (int j = i; j <= L->last; j++) {
L->Data[i] = L->Data[i + 1];
}
L->last--;
return;
}
//按序号查找
ElementType FindKth(int K, List L) //返回下标为 K 的当前值
{
if (K<0 || K > L->last)
{
cout << "位置越界,不存在元素L->Date[" << K << "]" << endl;
return -2;
}
return L->Data[K];
}
int Length(List L) //返回顺序表的长度
{
return L->last + 1;
}
int main() {
int i = 0;
L = MakeEmpty();
Insert(11, 0, L);
printf("在线性表L-Data[0]插入11\n");
Insert(25, 0, L);
printf("在线性表L-Data[0]插入25\n");
Insert(33, 0, L);
printf("在线性表L-Data[0]插入33\n");
Insert(77, 0, L);
printf("在线性表L-Data[0]插入77\n");
printf("此时的线性表为:");
for (i = 0; i < Length(L); i++)
printf("%d ", L->Data[i]);
printf("\n");
printf("查找值为12的下标是:%d\n", Find(12, L));
printf("下标为3的线性表的值是:%d\n", FindKth(3, L));
Delete(2, L);
printf("删除线性表中下标为2的元素\n");
Delete(2, L);
printf("删除线性表中下标为2的元素\n");
printf("此时的线性表为:");
for (i = 0; i < Length(L); i++)
printf("%d ", L->Data[i]);
printf("\n");
return 0;
}
2. 线性表的链表存储实现
malloc申请空间
free释放空间
/*2. 线性表的链表存储实现*/
#include<stdio.h>
#include<malloc.h>
#include<iostream>
using namespace std;
typedef int ElementType;
typedef struct LNode *List;
struct LNode {
ElementType Data;
List Next;
};
List L;
List MakeEmpty(); //初始化链表
int Length(List L); // 以遍历链表的方法求链表长度
List FindKth(int K, List L); // 按序号查找
List Find(ElementType X, List L); // 按值查找
List Insert(ElementType X, int i, List L); //将 X 插入到第 i-1(i>0) 个结点之后
List Delete(int i, List L); // 删除第 i(i>0) 个结点
void Print(List L); // 输出链表元素
List MakeEmpty()//初始化链表
{
List L;
L = (List)malloc(sizeof(struct LNode));
L = NULL;
return L;
}
int Length(List L)// 以遍历链表的方法求链表长度
{
List p = L;
int len = 0;
while (p)
{
p = p->Next;
len++;
}
return len;
}
List FindKth(int K, List L) // 按序号查找 ,返回指针,指向该值
{
List p = L;
int i = 1;
while (p && i < K) {
p = p->Next;
i++;
}
if (i == K)return p;
else return NULL;
}
List Find(ElementType X, List L) // 按值查找
{
List p = L;
int i = 1;
while (p && p->Data != X)
{
p = p->Next;
}
return p;
}
/* 插入
1. 用 s 指向一个新的结点
2. 用 p 指向链表的第 i - 1 个结点
3. s->Next = p->Next,将 s 的下一个结点指向 p 的下一个结点
4. p->Next = s,将 p 的下一结点改为 s */
List Insert(ElementType X, int i, List L) //将 X 插入到第 i-1(i>0) 个结点之后
{
List p, s;
if (i == 1) //当插入在表头时
{
//申请空间
s = (List)malloc(sizeof(struct LNode)); //申请空间
s->Data = X;
s->Next = L;
return s;
}
p = FindKth(i-1, L); //p 指向 i-1 个结点
if (p == NULL)
{
cout << "参数错误" << endl;
return NULL;
}
else
{
s = (List)malloc(sizeof(struct LNode)); //申请空间
s->Data = X;
s->Next = p->Next;
p->Next = s;
return L;
}
}
/* 删除
1. 用 p 指向链表的第 i-1 个结点
2. 用 s 指向要被删除的的第 i 个结点
3. p->Next = s->Next,p 指针指向 s 后面
4. free(s),释放空间
*/
List Delete(int i, List L) // 删除第 i(i>0) 个结点
{
List p, s;
if (i == 1) //删除的是第一个结点 //此时还需要讨论 线性表是否为空
{
if (L) {
s = L->Next;
free(L);
return s;
}
else { //记得讨论 为空的情况
return NULL;
}
}
p = FindKth(i - 1, L);
if (p == NULL) {
cout << "参数错误" << endl;
return NULL;
}
else if(p->Next == NULL)
{
cout << "参数错误" << endl;
return NULL;
}
else
{
s = p->Next->Next;
p->Next = s;
free(p->Next);
return L;
}
}
void Print(List L) // 输出链表元素 //需要加入判断,链表是否为空
{
List p;
int flag = 1;
p = L;
cout << "当前链表为:" << endl;
while (p) {
cout << p->Data << endl;
p = p->Next;
flag = 0;
}
if (flag) {
cout << "NULL" << endl;
}
}
int main() {
L = MakeEmpty();
Print(L);
L = Insert(11, 1, L);
L = Insert(25, 1, L);
L = Insert(33, 2, L);
L = Insert(77, 3, L);
Print(L);
printf("当前链表长度为:%d\n", Length(L));
printf("此时链表中第二个结点的值是:%d\n", FindKth(2, L)->Data);
printf("查找22是否在该链表中:");
if (Find(22, L))
printf("是!\n");
else
printf("否!\n");
printf("查找33是否在该链表中:");
if (Find(33, L))
printf("是!\n");
else
printf("否!\n");
L = Delete(1, L);
L = Delete(3, L);
printf("----------删除后-----\n");
Print(L);
return 0;
}
题目
2-1 两个有序链表序列的合并
注:
链表最好表头不存数据,方便操作(合并 、清空等)
记录好头指针 以区别 Next之后的指针
List Merge(List L1, List L2)
{
List head;
List L;
L = (List)malloc(sizeof(struct Node));
L->Next = NULL;
head = L;
List p1, p2;
p1 = L1->Next;
p2 = L2->Next;
while (p1&&p2) {
if (p1->Data < p2->Data)
{
L->Next = p1;
p1 = p1->Next;
}
else if (p1->Data >= p2->Data)
{
L->Next = p2;
p2 = p2->Next;
}
L = L->Next; //L后移
}
if (p1)
{
L->Next = p1;
}
if (p2)
{
L->Next = p2;
}
//将 L1 L2置空
L1->Next = NULL;
L2->Next = NULL;
return head;
}
自写了Read等
#include<iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;
/*裁判测试程序样例:*/
#include <stdio.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print(List L); /* 细节在此不表;空链表将输出NULL */
List Merge(List L1, List L2);
/* 你的代码将被嵌在这里 */
//初始化链表
List MakeEmpty()
{
List L;
L = (List)malloc(sizeof(struct Node));
L = NULL;
return L;
}
//按序号查找,返回指针,指向该值
List FindKth(int K, List L)
{
List p = L;
int i=1;
while (p && K > i) //K=i时 或者 p不存在,跳出循环
{
p = p->Next;
i++;
}
if (i == K)return p;
else return NULL;
}
//插入
List Insert(ElementType X, int i, List L) //!!!切记要申请空间
{
List p, s;
if (i == 1) //插入在表头时
{
s = (List)malloc(sizeof(struct Node));
s->Data = X;
s->Next = L;
return s;
}
p = FindKth(i - 1,L);
if (p == NULL) //插入错误
{
cout << "参数错误,插入不合规" << endl;
return NULL;
}
else {
s = (List)malloc(sizeof(struct Node));
s->Data = X;
s->Next = p->Next;
p->Next = s;
return L;
}
}
void Print(List L)// 输出链表元素
{
List p;
int flag = 1; //!!打印标识符,区分空表和打印完成
p = L;
while (p)
{
cout << p->Data << " ";
p = p->Next;
flag = 0;
}
if (flag == 1)cout << "链表为空" << endl;
else cout << "打印完成" << endl;
}
/*读取链表*/
List Read() //!!!链表头结点一般不存储数据
{
int len;
int a[1000];
cout << "输入个数:";
cin >> len;
for (int i = 0; i <len; i++)
{
cin >> a[i];
}
//创建链表
List L;
L = MakeEmpty();
for (int i = 0; i < len; i++)
{
L = Insert(a[i], i+1 , L);
}
cout << "链表为:" ;
Print(L);
return L;
}
/*合并链表*/
List Merge(List L1, List L2)
{
List L;
L = (List)malloc(sizeof(struct Node));
List p1,p2, head;
head = L;
p1 = L1->Next;
p2 = L2->Next;
//后续合并
while (p1 && p2)
{
if (p1->Data <= p2->Data)
{
L->Next = p1;
p1 = p1->Next;
}
else
{
L->Next = p2;
p2 = p2->Next;
}
L = L->Next;
}
if (p1)L->Next = p1;
if (p2)L->Next = p2;
L1->Next = NULL; //合并后 L1 L2 为 NULL
L2->Next = NULL;
return head; //注意!!,返回的是 头指针,而不是L,L已经后移了
}
int main()
{
List L1, L2, L;
L1 = Read();
L2 = Read();
L = Merge(L1, L2);
Print(L);
Print(L1);
Print(L2);
return 0;
}
2-2 一元多项式的乘法与加法运算
注:
加法 中注意排序,高次项在前
乘法基于加法,先乘 后调用加法,循环多次(基于P2个数);
后续结点 初始化设为空,不然报错,
若把初始结点设为空,则成了空指针;
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
using namespace std;
typedef struct TNode *List;
struct TNode
{
int x;
int z;
List Next;
};
List Input()
{
List L = (List)malloc(sizeof(struct TNode));
List head;
head = L;
L->Next = NULL;
int N;
int x, z;
cin >> N;
for (int i = 0; i < N; i++)
{
cin >> x >> z;
List t = (List)malloc(sizeof(struct TNode));
t->x = x;
t->z = z;
//L = L->Next;
//L= t;
L->Next = t;
L = L->Next;
L->Next = NULL;
}
return head;
}
void Print(List T)
{
int flag = 1;// 零多项式 打印 //指数全为0 时不变
if (!T) { return; }
T = T->Next;
while (T)
{
if (T->x != 0) //系数不为0时
{
flag = 0;
cout << T->x << " " << T->z;
T = T->Next;
if (T)cout << " ";
}
else if (T->x == 0) //系数为0时,跳过打印
{
T = T->Next;
}
}
if (flag == 1) //系数全为0时
{
cout << "0 0";
}
}
//加法运算
List Addition(List L1, List L2)
{
List L = (List)malloc(sizeof(struct TNode));
List head;
head = L;
L->Next = NULL;
List p1;
List p2;
p1 = L1->Next;
p2 = L2->Next;
while (p1 && p2)
{
List t = (List)malloc(sizeof(struct TNode));
if (p1->z == p2->z)
{
t->x = p1->x + p2->x;
t->z = p1->z;
p2 = p2->Next;
p1 = p1->Next;
}
else if (p1->z > p2->z)
{
t->x = p1->x;
t->z = p1->z;
p1 = p1->Next;
}
else if (p1->z < p2->z)
{
t->x = p2->x;
t->z = p2->z;
p2 = p2->Next;
}
L->Next =t;
L = L->Next;
L->Next = NULL;
}
if (p1)L->Next = p1;
else if (p2)L->Next = p2;
return head;
}
//乘法
List Mutiplulation(List L1, List L2)
{
List L = (List)malloc(sizeof(struct TNode));
/*List head;
head = L;*/
L->Next = NULL;
List p1;
List p2;
p1 = L1->Next;
p2 = L2->Next;
while (p2) {
List t1 = (List)malloc(sizeof(struct TNode));
List t1_head = t1;
while (p1) { //相乘
List t2 = (List)malloc(sizeof(struct TNode));
t2->x = p1->x * p2->x;
t2->z = p1->z+p2->z;
t1->Next = t2;
t1 = t1->Next;
t1->Next = NULL;
p1 = p1->Next;
}
p1 = L1->Next;
//相加
L = Addition(L, t1_head);
p2 = p2->Next;
}
return L; //这里不是返回head,因为head为空。L = Addition(L, t1_head);中L重新生成了好几次,而head为最开始的空L。
}
int main()
{
List T1 = Input();
List T2 = Input();
List Ta = Addition(T1, T2);
List Tm = Mutiplulation(T1, T2);
Print(Tm);
cout << endl;
Print(Ta);
return 0;
}
2-3 Reversing Linked List
02-线性结构3 Reversing Linked List
思路:
先写入,通过data[address]和next[address]把数据整合起来,然后按顺序捋顺,同时记录sum,并按顺序将地址写入List[i]。
之后只需要 反转 地址(即 list),然后输出
List[i],Data[List[i]],List[i+1]
注:最后一个不是输出 Next[address],而是输出新的 下一个顺序地址,List[i+1]
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
#define MAXSIZE 100005
int Data[MAXSIZE];
int Next[MAXSIZE];
int List[MAXSIZE];
int main()
{
int FirstAddress,N, K;
cin >> FirstAddress >> N >> K;
int addresstmp, datatmp, nexttmp;
for (int i = 0; i < N; i++) //将 address, data, next 写入,并对应起来
{
cin>> addresstmp>> datatmp>> nexttmp;
Data[addresstmp] = datatmp;
Next[addresstmp] = nexttmp;
}
int sum = 0; //用list 按顺序 记录 address
while (FirstAddress != -1)
{
List[sum] = FirstAddress;
FirstAddress = Next[FirstAddress];
sum++; //用sum 记录链表中的结点,避免【有多余结点不在链表上】的情况
}
//反转
int tmpadd;
int period = N / K;
for (int i = 0; i < period; i++) //反转链表,在 i*K 和 (i+1)*K-1 之间反转需要反转,总共需要反转period次
{
for (int j = 0; j < K / 2; j++) //每个 反转小组 内部,需要反转的次数
{
//int j = 0;
tmpadd = List[i*K + j];
List[i*K + j] = List[(i + 1)*K - 1 - j];
List[(i + 1)*K - 1 - j] = tmpadd;
}
}
//打印
for(int i=0;i<sum-1;i++)
printf("%05d %d %05d\n", List[i],Data[List[i]],List[i+1]); //下一步地址用List[i+1],而不是Next[],后者为原来的顺序
printf("%05d %d -1", List[sum - 1], Data[List[sum - 1]]);
}
注:reverse函数用于反转在(first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数没有返回值 。
使用方法:
1.交换vector容器中元素顺序
vector<int> v = {5,4,3,2,1}; reverse(v.begin(),v.end());//v的值为1,2,3,4,5
v.end() 不在v之中
2.交换string字符串中元素的顺序
string str="www.mathor.top"; reverse(str.begin(),str.end());//str结果为pot.rohtam.www
3.交换字符数组char[]中元素的顺序
char a[101] = “hello world”; reverse(a,a+strlen(a));
不包括last指向的元素
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<vector>
#include <algorithm>
using namespace std;
#define MAXLEN 100001
struct node {
int address;
int data;
int next;
};
typedef struct node Node;
Node nodes[MAXLEN];
vector<Node> nodelist;
int main() {
//输入
int firstAdd, N, K;
cin >> firstAdd >> N >> K;
while (N--) {
Node n;
cin >> n.address >> n.data >> n.next;
nodes[n.address] = n;
}
//按顺序 输入到nodelist
int address = firstAdd;
while (address != -1) {
nodelist.push_back(nodes[address]);
address = nodes[address].next; //地址变为下一个
}
//利用C++的reverse函数 反转
int len = nodelist.size();
int period = len / K; //反转周期
for (int i = 1; i <= period; i++) { //reverse函数用于反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),
int head = (i - 1)*K;
int end = K * i ;
reverse(nodelist.begin() + head, nodelist.begin() + end); //反转 不包含end
}
//打印
for (int i = 0; i < len - 1; i++) {
printf("%05d %d %05d\n", nodelist[i].address, nodelist[i].data, nodelist[i + 1].address);
}
printf("%05d %d %d", nodelist[len-1].address, nodelist[len-1].data, -1);
return 0;
}
2-4 Pop Sequence
两个限制条件
1.栈里面的数字是否超过给定的栈的长度,即是否溢出。
2.栈里面的数字输出顺序是否正确 ,即 当一个数字已经出栈时,证明 其它未出栈且比它小的数字必须在栈里面而且必须满足大在上的顺序.注:压入栈的顺序为1、2、3、4、5、6、7
c++ stl栈stack的头文件为: \#include<stack> stack<int> S;//声明一个对象 S.empty();//栈空返回true 否则false int x=S.size();//返回栈中元素个数于x S.pop();//移除栈顶元素 S.push(x);将x置于栈顶 x=S.top();返回栈顶元素
#include<iostream>
#include<stack>
using namespace std;
#define Maxsize 1000
int main()
{
int array[Maxsize];
stack <int> s;
int M, N, K;
cin >> M >> N >> K;
int tmp;
for (int i = 0; i < K; i++) //总共判断五次,判断结果写入array
{
int judge = 1;
int n = 0;
int j = 1; //从1到N
while (!s.empty())s.pop(); //清空栈
//测试
//cout << "第 " <<i<<"次输入" << endl;
while (n<N) //输入一组数据,每组 N个值,进行判断
{
cin >> tmp; //输入 出栈元素,将比它小的压入栈,进行判断
while (j <= tmp) //入栈,直到栈顶元素 变为tmp,当j>tmp 时,不入栈,直接出栈
{
s.push(j);
//测试
//cout << "入栈: " << j << endl;
j++;
}
if (s.size() > M) { //判断 是否溢出
judge = 0;
}
//出栈
int x = s.top();
if (x != tmp) { //判断 栈顶出栈元素是否为 tmp
judge = 0;
}
s.pop();
n++;
//cout << "出栈: " << x << endl;
}
if (judge == 1)array[i] = 1;
else if (judge == 0)array[i] = 0;
//cout << "判断结果: " << array[i] << endl;
}
//输出结果
for (int i = 0; i < K-1; i++)
{
if (array[i] == 1)cout << "YES" << endl;
else if (array[i] == 0)cout << "NO" << endl;
}
if (array[K - 1] == 1)cout << "YES" ;
else if (array[K - 1] == 0)cout << "NO" ;
return 0;
}
3 树
3.1 树的定义
inorder binary tree traversal 二叉树的中序遍历
postorder traversal sequence of this tree 该树的 后序遍历序列
a non-recursive way 一种非递归的方式
3.2 二叉树
题目
3-1 树的同构
分析:
考虑实现寻找根结点,设定 root 变量为初值,随着每次读取一行结点信息,root 累加行号,遇到左右儿子结点编号减取相应的数值即可,最终 root 的值就是该树的根结点
找到根结点,考虑如何判断是否同构,还是继续观察,所谓"同构"就是还是儿子结点还是儿子结点,不变,只是左儿子右儿子可能互换位置
所以设定规则如下:1、如果结点都是空,返回 true
2、如果只有某一个结点为空,返回 false
3、如果结点值不同,返回 false
4、如果左儿子都不为空且值相等,分别进入左右儿子结点
5、否则调换左右儿子位置进入左右儿子结点
注意:’-'为字符,所以
char left, right;
,但是root 为 int,所以要把 left, right的字符转换成数字,用到了 c-‘0’,将字符 转换成数字,再与 root相加。
同理, c+‘0’,将 数字 转换成字符
TEST
注1:TNode T1[MAXSIZE] 用 结构体数组 来构建一棵树
注2:当 输入空树时,将root置为空,否则Judge()无法判断
注3:判断是否同构的函数中,【判断空】需要 用 【坐标】为null表示,均 不为空之后,用 【data】确定 值是否相等,前者为int 后者 为char 。且为空的坐标,没有值,避免进行值判断。故要先判断是否为空(两个根为空,1个为空,均不为空)。
#include<iostream>
#include<string>
using namespace std;
#define null -1
#define MAXSIZE 10
typedef struct TreeNode TNode;
struct TreeNode
{
int left; //坐标用数字表示, 输入时将 临时字符串 转换成数字后输入
int right; //- 转换成 -1
char data;
};
TNode T1[MAXSIZE];
TNode T2[MAXSIZE];
int Create(TNode T[MAXSIZE])
{
int N;
char tmpleft, tmpright, data;
int root = 0;
cin >> N;
if (N == 0)return root = null; //!!当 输入空树时,将root置为空,否则Judge()无法判断
for (int i=0; i < N; i++)
{
//while (!T) return;
cin >> data >> tmpleft >> tmpright;
if (tmpleft != '-')
{
root -= tmpleft - '0';
T[i].left = tmpleft - '0';
}
else if (tmpleft == '-')
{
T[i].left= null;
}
if (tmpright != '-')
{
root -= tmpright - '0';
T[i].right = tmpright - '0';
}
else if (tmpright == '-')
{
T[i].right = null;
}
T[i].data = data;
root += i;
}
return root;
}
bool Judge(int R1, int R2)//!!!【判断空】需要 用 【坐标】为null表示,均 不为空之后,用 【data】确定 值是否相等,前者为int 后者 为char 。且为空的坐标,没有值,故要先判断是否为空(两个根为空,1个为空,均不为空)。
{
//递归结束
if (R1 == null && R2 == null) return 1; // 2个根均为空,【坐标】为-1/null
if ((R1 == null && R2 != null)|| (R1 != null && R2 == null)) return 0; // 只有1个根为空,【坐标】为-1/null
if (T1[R1].data != T2[R2].data) return 0; // 根的值 不等
//进一步判断
else if (T1[R1].data == T2[R2].data) // 根的值 相等
{
//判断 左子树 是否 存在且相等
// 2个左子树均为空, 则 递归判断 右子树
if (T1[R1].left == null && T2[R2].left == null)
return Judge(T1[R1].right, T2[R2].right); //记得return
//左子树【均不为空】且值【相等】, 则进一步递归 判断 左右子树
if ((T1[R1].left != null && T2[R2].left != null) && (T1[T1[R1].left].data == T2[T2[R2].left].data))
return Judge(T1[R1].left, T2[R2].left) && Judge(T1[R1].right, T2[R2].right);
// 其他情况 其中【一个左子树为空】 或者 均不为空 但【不相等】, 则 递归 且 交换判断
else
return Judge(T1[R1].left, T2[R2].right) && Judge(T1[R1].right, T2[R2].left);
}
}
int main()
{
int T1_root = Create(T1);
int T2_root = Create(T2);
bool j = Judge(T1_root, T2_root);
if (j == 1)cout << "Yes";
else cout << "No";
return 0;
}
3-2 List Leaves
分析:参考 层序遍历 ,通过队列实现,打印左右儿子为空的 结点。
void LevelOrderTraversal(BinTree BT){ queue<BinTree> q; // 创建队列 BinTree T; if(!BT) return; q.push(BT); // BT 入队 while(!q.empty()){ T = q.front(); // 访问队首元素 q.pop(); // 出队 printf("%d",T->Data); if(T->Left) // 如果存在左儿子结点 q.push(T->Left); // 入队 if(T->Right) q.push(T->Right); } }
注:空 设为 null (-1),而不是 0,也不能
if (t.right && t.right != null)
去判断为不为空,因为t.right 可能为0,if中0为 false。
//test
#include<iostream>
#include<queue>
#define null -1
using namespace std;
typedef struct TreeNode TNode;
struct TreeNode
{
int left;
int right;
int index;
};
TNode T[10];
int Create(TNode T[])
{
int root = 0;
int N;
char lefttmp, righttmp;
cin >> N;
if (N == 0) return null;
for (int i = 0; i < N; i++)
{
cin >> lefttmp >> righttmp;
if (lefttmp != '-')
{
T[i].left = lefttmp - '0';
root -= T[i].left;
}
else if (lefttmp == '-')
{
T[i].left = null;
}
if (righttmp != '-')
{
T[i].right = righttmp - '0';
root -= T[i].right;
}
else if (righttmp == '-')
{
T[i].right = null;
}
root += i;
T[i].index = i;
}
return root;
}
int main()
{
int r = Create(T);
queue <TNode> q;
TNode t;
int j = -1;
int array[10];
while (!q.empty())q.pop();
if (!T)return 0;
q.push(T[r]); //根结点入队 //层序遍历法,按每层顺序打印无左右结点的值
while (!q.empty())
{
t = q.front();
q.pop();
if (t.left != null) q.push(T[t.left]);
if (t.right != null) q.push(T[t.right]);
if (t.left == null && t.right == null)
{
j++;
array[j] = t.index;
}
}
for (int i = 0; i < j; i++)
{
cout << array[i] << " ";
}
cout << array[j];
return 0;
}
#include<iostream>
#include<stdio.h>
#include<malloc.h>
#include<queue>
using namespace std;
#define null -1 //用null = -1 代表空,避免 值为0时判定为空
typedef struct TreeNode {
int data;
int left, right;
}TNode;
TNode T[10];
int Create(struct TreeNode T[])
{
char left, right;
int root = 0;
int n;
cin >> n;
while (!n)return null;
for (int i = 0; i < n; i++) {
cin >> left >> right;
T[i].data = i;
if (left != '-') {
T[i].left = left - '0';
root -= T[i].left;
}
else T[i].left = null;
if (right != '-') {
T[i].right = right - '0';
root -= T[i].right;
}
else T[i].right = null;
root += i;
}
return root;
}
//层序遍历
void LevelorderTraversal(int root)
{
queue<struct TreeNode> q; // 创建队列
TNode t;
// 如果树为空,直接打印
//if (!T)return; //为空 是 -
if (root == null) {
cout << "-";
return;
}
//0.根结点入队
q.push(T[root]);
while (!q.empty()) {
//从队列中取出一个 队首元素
t = q.front(); // 访问队首元素
q.pop();
//访问该元素所指结点
//若该元素所指结点的左孩子结点非空,左孩子结点入队
if (t.left != null)
{
q.push(T[t.left]);
//cout << "T[" << t.left << "]入队:" << T[t.left].data << endl;
}
//若该元素所指结点的右孩子结点非空,右孩子结点入队
//if (t.right && t.right != null)错误 //当t.right 为0时 会被认为 不存在
if ( t.right != null)
{
q.push(T[t.right]);
//cout << "T[" << t.right << "]入队:" << T[t.right].data << endl;
}
//如果其没有左右孩子
if (t.left == null && t.right == null)
{
cout << t.data << " ";
}
}
}
int main() {
int r = Create(T);
LevelorderTraversal(r);
return 0;
}
3-3 Tree Traversals Again
分析:push可以得出先序遍历,栈和pop可以得出中序遍历,通过递归的方式,确定根结点,从而将后序遍历确定
注1:递归函数 传入的是 【数组的首位坐标】,而不是传入数组,各个遍历数组在全局创建。
注2:while(N–)中 N数值发生了变化,后续需要注意。
注3:
if (p == InorderArray[ j+InL])break;
中不能忘了加 中序遍历的首位 InL,确保递归正确性。
//test
#include <iostream>
#include<string>
#include<stack>
using namespace std;
#define MAXSIZE 30
//typedef struct TreeNode Tnode;
//struct TreeNode
//{
//
//};
int InorderArray[MAXSIZE];
int PreorderAarray[MAXSIZE];
int PostorderArray[MAXSIZE];
int N;
void Input()
{
stack<int> s;
string p;
int q;
int j = -1;
int k = -1;
cin >> N;
if (N == 0)return;
//清空堆栈
while (!s.empty())s.pop();
for (int i = 0; i < 2 * N; i++)
{
cin >> p;
if (p == "Push")
{
cin >> q;
j++;
PreorderAarray[j] = q;
s.push(q);
}
else if (p == "Pop")
{
q = s.top();
s.pop();
k++;
InorderArray[k] = q;
}
}
//cout << "打印:";
//for (int i = 0; i <= j; i++)
//{
// cout << PreorderAarray[i] << " " << InorderArray[i] << endl;
//}
}
//递归
void Postorder(int PreL, int InL,int PostL,int n) //!!!递归传入的是 数组的首位坐标,而不是传入数组
{
int m = n;
//递归结束条件
if (n == 0)return;
if (n == 1)
{
PostorderArray[PostL] = PreorderAarray[PreL];
return;
}
//写入数据到 后序数组
int p = PreorderAarray[PreL];
PostorderArray[PostL + n - 1] = p;
int L = 0;
int R = 0;
//for(int i=0;i<n;i++)
//{
// if (p == InorderArray[i+InL])
// {
// L = i;
// R = m - i - 1;
// }
//
//}
int j = 0;
while (m--)
{
if (p == InorderArray[ j+InL])break; //错:p == InorderArray[j] ,忘了加 初始PreL,导致递归结果出错
j++;
}
L = j;
R = n - j - 1; //错:R = m - j - 1...此时 m已经因为while(m--)变了,会造成R值不对,递归无法停止
//递归
//cout << "PreL+1, InL, PostL, L" << PreL + 1 << InL << PostL << L;
Postorder(PreL+1, InL, PostL, L);
//cout << "PreL + 1+L, InL + 1 + L, PostL + L, R" << PreL + 1 + L << InL + 1 + L << PostL + L << R;
Postorder(PreL + 1+L, InL + 1 + L, PostL + L, R);
}
int main()
{
Input();
Postorder(0, 0, 0, N);
for (int i = 0; i < N-1; i++)
{
cout << PostorderArray[i] << " ";
}
cout << PostorderArray[N - 1];
return 0;
}
4-4 是否同一棵二叉搜索树
注1:对于 二叉搜索树,它的中序遍历都是 按照按从小到大顺序排列。
注2:
//test
#include<iostream>
#include<string>
#include<malloc.h>
using namespace std;
typedef struct TreeNode* Bintree;
struct TreeNode {
int data;
Bintree left;
Bintree right;
};
Bintree Insert(Bintree T, int X)
{
if (!T)
{
T = (Bintree)malloc(sizeof(struct TreeNode)); //!!
T->data = X;
T->left = NULL;
T->right = NULL;
}
else {
if (T->data < X)T->right = Insert(T->right, X);
else if (T->data > X)T->left = Insert(T->left, X);
}
return T;
}
//string Preorder(Bintree T)
//{
// string s;
// if (T)
// {
// cout << "s:" << s << endl;
// s += T->data + '0';
// Preorder(T->left);
// Preorder(T->right);
// }
// return s;
//}
// 前序遍历
void PreOrderTraversal(Bintree BST, string &s) {
if (BST) {
s += BST->data + '0'; // 将结点值保存进字符串
PreOrderTraversal(BST->left, s); // 进入左子树
PreOrderTraversal(BST->right, s); // 进入右子树
}
}
int main()
{
int N, L, Q;
while (1)
{
cin >> N;
if (N == 0)break;
else {
cin >> L;
string array[100];
for (int i = 0; i <= L; i++)
{
Bintree T;
/*T = (Bintree)malloc(sizeof(struct TreeNode));
T->left = NULL;
T->right = NULL;*/
T = NULL;
for (int j = 0; j < N; j++)
{
cin >> Q;
T = Insert(T, Q);
}
/*string s = Preorder(T);*/
string s;
PreOrderTraversal(T, s);
array[i] = s;
}
for (int i = 0; i < L; i++)
{
if (array[0] == array[i + 1])cout << "Yes";
else cout << "No";
cout << endl;
}
}
}
return 0;
}
4-树5 Root of AVL Tree
思路:插入的时候 就判断是否需要旋转,LL和RR旋转的时候更新树的高度即可。
注: Insert()函数 中(!T)满足时,代表插入叶节点,即递归结束的标志,不能 Tnode T 重新创建(之前其父节点已经给他赋为NULL了),同时记录高度为0。
具体插入时,需要判断 高度差是否为2,为2则进行AVL旋转。
结点的高度,通过递归的方式,获取左右子树高度最大值 并 +1 即可。
故 节点为NULL时,其高度设为-1,叶节点高度 = -1 +1 =0.
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<malloc.h>
using namespace std;
typedef struct TreeNode *Tnode;
typedef Tnode AVLTree;
typedef int ElementType;
struct TreeNode
{
Tnode left;
Tnode right;
int data;
int height;
};
int getheight(AVLTree A)
{
if(!A) return -1;
else return A->height;
}
int Max(int a, int b)
{
return a > b ? a : b;
}
AVLTree RRRotation(AVLTree A)
{
AVLTree B = A->right;
A->right = B->left;
B->left = A;
//计算高度
A->height = Max(getheight(A->left), getheight(A->right)) + 1;
B->height = Max(A->height, getheight(B->right)) + 1;
return B;
}
AVLTree LLRotation(AVLTree A)
{
AVLTree B = A->left;
A->left = B->right;
B->right = A;
//计算高度
A->height = Max(getheight(A->left), getheight(A->right)) + 1;
B->height = Max(A->height, getheight(B->left))+ 1;
return B;
}
AVLTree RLRotation(AVLTree A)
{
//LL
A->right = LLRotation(A->right);
//RR
return RRRotation(A);
}
AVLTree LRRotation(AVLTree A)
{
A->left = RRRotation(A->left);
return LLRotation(A);
}
Tnode Insert(Tnode T, ElementType X)
{
if (!T)
{
T = (Tnode)malloc(sizeof(struct TreeNode)); //!!叶节点。递归结束的条件,不需要Tnode T
T->data = X;
T->left = NULL;
T->right = NULL;
T->height = 0; //叶子节点高度为0
}
else {
if (X > T->data)
{
T->right= Insert(T->right, X); //插入右边
if(getheight(T->right)-getheight(T->left) == 2 ) //高度差为2
if (X > T->right->data) //RR旋转
{
T = RRRotation(T); //!!!要设返回值 T = _____
}
else if ((X < T->right->data)) //RL旋转
{
T = RLRotation(T);
}
}
else if (X < T->data)
{
T->left= Insert(T->left, X);
if (getheight(T->right) - getheight(T->left) == -2) //高度差为2
if (X < T->left->data) //LL旋转
{
T = LLRotation(T);
}
else if ((X > T->left->data)) //LR旋转
{
T = LRRotation(T);
}
}
//错:T->height += 1;
}
T->height = Max(getheight(T->left), getheight(T->right)) + 1;
return T;
}
void PreorderTraversal(Tnode T)
{
if (T)
{
cout << T->data << " "<<T->height << endl;
PreorderTraversal(T->left);
PreorderTraversal(T->right);
}
}
int main()
{
int N,L;
cin >> N;
Tnode T;
T = NULL;
for (int i=0; i < N; i++)
{
cin >> L;
T = Insert(T, L);
if (i==0)
{
T->height = 0;
}
}
//PreorderTraversal(T);
cout << T->data;
}
4-树6 Complete Binary Search Tree
思路:先按 中序遍历按从小到大顺序存入 数组,再 通过递归方式,求得 左右子树个数,利用数组中的 角标递归插入到 完全二叉树中。
参考:[3-3 Tree Traversals Again],更进一步,不需要两次建树,利用sort函数给数组排序,利用 结构体数组 ,往层序遍历的数组 中写入 数组即可。《数据结构》04-树6 Complete Binary Search Tree
完美二叉树 最后层个数 2^(n-1)
结点总数 2^n - 1 ;前(n-1)层结点总数2^(n-1) - 1
n = log(S) / log(2) + 1; s1 = pow(2 , (n - 1)) - 1; //前n-1层 个数 s2 = S - s1; //最后一层个数 s3 = pow(2 , (n - 1)); //最后层满状态的 个数
层序遍历:
遍历过程:从上至下,从左至右访问所有结点
队列实现过程:
0.根结点入队
1.从队列中取出一个元素
2.访问该元素所指结点
3.若该元素所指结点的左孩子结点非空,左孩子结点入队
4.若该元素所指结点的右孩子结点非空,右孩子结点入队
5.循环 1 - 4,直到队列中为空
void LevelOrderTraversal(BinTree BT){ queue<BinTree> q; // 创建队列 BinTree T; if(!BT) return; q.push(BT); // BT 入队 while(!q.empty()){ T = q.front(); // 访问队首元素 q.pop(); // 出队 printf("%d",T->Data); if(T->Left) // 如果存在左儿子结点 q.push(T->Left); // 入队 if(T->Right) q.push(T->Right); } }
注:
创建 队列
创建新的 树,用作 取队首的树,并使 新树左右子节点(如果存在)入队。
#include<iostream>
#include<malloc.h>
#include<queue>
#include<math.h>
using namespace std;
typedef struct BinarySearchTree *BST;
struct BinarySearchTree {
BST left;
BST right;
int data;
};
int N;
int i = 0;
int qi = 0;
int b[1000];
int a[1000];
//中序
void InorderTraversal(BST T)
{
if(T)
{
InorderTraversal(T->left);
//cout << T->data;
if (i < N)
{
a[i] = T->data;
i++;
}
InorderTraversal(T->right);
}
}
//层序 !!!!
void SequenceTraversal(BST BT)
{
queue<BST> q; // 创建 <BST> 队列
BST T; // 创建 树,用作取队首元素,并使 其左右子节点入队。
if (!BT)
return;
q.push(BT); // BT 入队
while (!q.empty()) {
T = q.front(); // 访问队首元素
q.pop(); // 出队
//printf("%d ", T->data);
if (qi < N)
{
b[qi] = T->data;
qi++;
}
if (T->left) // 如果存在左儿子结点
q.push(T->left); // 入队
if (T->right)
q.push(T->right);
}
}
//插入
BST Insert(BST T, int X)
{
if (!T)
{
T = (BST)malloc(sizeof(struct BinarySearchTree));
T->data = X;
//cout << "插入 " << X << endl;
T->left = NULL;
T->right = NULL;
}
else {
if (X < T->data) T->left = Insert(T->left, X);
else if (X > T->data) T->right = Insert(T->right, X);
}
return T;
}
//计算左子树和右子树 个数
int CalculateNumber(int S)
{
int n, R, s1, s2, s3;
n = log(S) / log(2) + 1;
s1 = pow(2 , (n - 1)) - 1; //前n-1层 个数
s2 = S - s1; //最后一层个数
s3 = pow(2 , (n - 1)); //最后层满状态的 个数
if (s2 <= (s3 / 2))
{
R = (pow(2 , (n - 1)) - 2) / 2;
}
else
{
R = (pow(2 , (n - 1)) - 2) / 2 + (s2 - s3 / 2);
}
return R;
}
//递归构建 完全二叉搜索树
BST CompleteBinaryTree(BST T,int j,int S)
{
if (S == 0)return NULL;
else if (S == 1)
{
//cout << "a[j]=" << a[j] << endl;
T = Insert(T, a[j]);
}
else if (S == 2)
{
//cout << "a[j+1]=" << a[j+1] << endl;
T = Insert(T, a[j + 1]);
//cout << "a[j]=" << a[j] << endl;
T = Insert(T, a[j]);
}
int R = CalculateNumber(S);
int L = S - R - 1;
//cout << "a[j+L]=" << a[j +L] << endl;
T = Insert(T, a[j + L ]);
T->left = CompleteBinaryTree(T->left, j, L);
T->right = CompleteBinaryTree(T->right, j+L+1, R);
return T;
}
int main()
{
BST T,CT;
int X;
T = NULL;
CT = NULL;
cin >> N;
//中序遍历 建树
/*for (int k = 0; k < N; k++)
{
cin >> X;
T = Insert(T, X);
}
InorderTraversal(T); */
//参考1: sort 函数给数组自动排序,不需要建树,不需要中序遍历
for (int i = 0; i < N; i++) {
cin >> a[i];
}
// 从小到大排序
sort(a, a + N);
CT = CompleteBinaryTree(CT,0,N);
SequenceTraversal(CT);
for (int i = 0; i < N - 1; i++)
{
cout << b[i] << " ";
}
cout << b[N-1] ;
}
3.3 堆
优先队列(priority Queue):特殊的"队列",取出元素的顺序是依照元素的优先级(关键字)大小,而不是元素进入队列的先后顺序,以完全二叉树存储
两个特性
结构性:用数组表示的完全二叉树
有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
“最大堆(MaxHeap)”,也称"大顶堆":最大值
“最小堆(MinHeap)”,也称"小顶堆":最小值
从根结点到任意结点路径上结点序列 是有序的。
堆 从下标为1的地方开始存放
题目
05-树7 堆中的路径
分析:考查最小堆的建立
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
typedef struct HeapStruct *MinHeap;
typedef int ElementType;
#define MinData -100000
#define MaxSize 1005
#define ERROR -1
struct HeapStruct
{
int Size;
int Capacity;
ElementType *Elements;
};
MinHeap Create(int Maxsize)
{
MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct));
H->Elements = (ElementType *)malloc((Maxsize + 1) * sizeof(struct HeapStruct));
H->Size = 0;
H->Capacity = Maxsize;
H->Elements[0] = MinData;
return H;
}
bool IsFull(MinHeap H)
{
return (H->Size == H->Capacity);
}
bool IsEmpty(MinHeap H)
{
//return !H->Elements[1];
return !H->Size;
}
void Insert(MinHeap H, ElementType X)
{
if (IsFull(H))
{
cout << "已满,不能插入" << endl;
//return false;
}
else
{
int i = ++H->Size;
for (; H->Elements[i / 2] > X; i /= 2) //第一次插入时 要有哨兵值 MinData
{
H->Elements[i] = H->Elements[i / 2];
}
H->Elements[i] = X;
//return true;
}
}
void PathTraversal(MinHeap H,int x)
{
for (; x >= 1; x /= 2)
{
if (x != 1)
{
cout << H->Elements[x] << " ";
}
else cout << H->Elements[x];
}
cout << endl;
}
int main()
{
int N, M;
int x, y;
cin >> N >> M;
MinHeap h = Create(N);
for (int i = 0; i < N; i++)
{
cin >> x;
Insert(h, x);
}
for (int i = 0; i < M-1; i++)
{
cin >> y;
PathTraversal(h, y);
}
cin >> y;
PathTraversal(h, y);
}
3.4并查集
题目
05-树8 File Transfer
分析:考察的是并查集的优化
核心在于"并"和"查"操作
- 并:将两个结点所属集合合并,在此用到按秩归并,即每次合并时,规模小的树作为子结点挂到规模大的树上
- 查:检查两个结点是否属于同一集合,在此用到路径压缩,即每次查找时,把所查找结点到根结点间一系列结点的值都直接挂到根结点上去
#include<cstdio>
#include<iostream>
#include<stdio.h>
using namespace std;
typedef int SetType;
#define MaxSize 10001
void Init(SetType s[],int n)
{
for (int i = 1; i <= n; i++)
{
s[i] = -1;
}
}
int FindRoot(SetType s[], int x)
{
if (s[x] < 0)
return x;
else
return s[x] = FindRoot(s, s[x]); //!!!尾递归
}
void Union(SetType s[], int x1, int x2) //传入根
{
if (s[x1] < s[x2]) //负数,越小,
{
s[x1] += s[x2];
s[x2] = x1;
}
else
{
s[x2] += s[x1];
s[x1] = x2;
}
}
//输入、连接
void Input_connection(SetType s[])
{
int x1, x2;
cin >> x1>> x2;
int r1 = FindRoot(s, x1);
int r2 = FindRoot(s, x2);
Union(s, r1, r2);
}
//测试
void Check_connection(SetType s[])
{
int x1, x2;
cin >> x1>> x2;
int r1 = FindRoot(s, x1);
int r2 = FindRoot(s, x2);
if (r1 == r2)cout << "yes" << endl;
else cout << "no" << endl;
}
//结束
void Stop_connection(SetType s[],int N)
{
int x1;
int x2 = 0;
for (int i = 1; i <= N; i++)
{
x1 = s[i];
if (x1 < 0)
{
x2++;
}
}
if (x2 == 1)
{
cout << "The network is connected.";
}
else
{
cout << "There are " << x2 << " components.";
}
}
int main()
{
int N;
char X = 0;
cin >> N;
SetType s[MaxSize];
Init(s, N);
while (X != 'S')
{
cin >> X;
if (X == 'I')Input_connection(s);
else if (X == 'C')Check_connection(s);
}
Stop_connection(s, N);
}
3.5哈夫曼树
题目
5-树9 Huffman Codes
思路:
1.通过 最小堆 的方式建立 huffmantree
2.求该huffmantree对应的 wpl
3.求输入的编码字符是是否满足 WPL 和 无二义性 两个条件
参考:
【C/C++】STL详解求取 string 前n个字符是否 和较短的 编码字符相等
string
string &assign(const string &s,int start,int n);把字符串s中从start开始的n个字符赋给当前字符串 例如以下代码: string str1, str2 = "War and Peace"; str1.assign( str2, 4, 3 ); cout << str1 << endl; 结果:and
map
map.insert(...); //往容器插入元素,返回pair<iterator,bool> map<int, string> mapStu; // 第一种 通过pair的方式插入对象 mapStu.insert(pair<int, string>(3, "小张")); // 第二种 通过pair的方式插入对象 mapStu.inset(make_pair(-1, "校长")); // 第三种 通过value_type的方式插入对象 mapStu.insert(map<int, string>::value_type(1, "小李")); // 第四种 通过数组的方式插入值 mapStu[3] = "小刘"; mapStu[5] = "小王";
计算string 某些字符个数
s = "123123"; int slen = count(s.begin(), s.end(),'0')+ count(s.begin(), s.end(), '1');
#include<iostream>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
#define MinData 0
#define HeapCapacity 64
typedef struct TreeNode *HuffmanTree;
struct TreeNode {
int weight;
HuffmanTree Left, Right;
};
typedef struct Heap* MinHeap;
struct Heap {
HuffmanTree* Data;
int Size;
};
struct HuffArray {
string str;
int slen;
};
/*建堆、建树*/
//初始建立huffman
HuffmanTree CreateHuffmanTree()
{
HuffmanTree H;
H = (HuffmanTree)malloc(sizeof(struct TreeNode));
H->weight = MinData;
H->Right = NULL;
H->Left = NULL;
return H;
}
//初始建立最小堆
MinHeap CreateMinHeap()
{
MinHeap H;
H = (MinHeap)malloc(sizeof(struct Heap));
H->Data = (HuffmanTree*)malloc(HeapCapacity * sizeof(struct TreeNode));
H->Size = 0;
HuffmanTree Huff = CreateHuffmanTree();
//哨兵
H->Data[0] = Huff;
return H;
}
//删除 最小堆 中 weight最小的 Huffmantree
HuffmanTree DeleteMin(MinHeap H) //删除根节点,用尾结点往上比较移动
{
int parent, child;
HuffmanTree HuffMin, Hufftmp;
HuffMin = H->Data[1]; //!!!根节点 为 [1]
Hufftmp = H->Data[H->Size--];
for (parent = 1; 2 * parent <= H->Size; parent = child)
{
child = 2 * parent;
/*选出最小的子树 与 被删的父节点比较*/
//当存在右子树,且其值 小于 左子树 时
if (child < H->Size&& H->Data[child]->weight > H->Data[child + 1]->weight)
child++; //替换为 右子树
//最小子树与 Hufftmp比较
if (H->Data[child]->weight > Hufftmp->weight)
break;
else
H->Data[parent] = H->Data[child]; //把 较小子树 提到 parent上
}
H->Data[parent] = Hufftmp;
return HuffMin;
}
//向 最小堆 中 插入 HuffmanTree
void InsertHeap(MinHeap H, HuffmanTree Huff)
{
int i = ++H->Size;
for (; H->Data[i / 2]->weight > Huff->weight; i /= 2)
{
H->Data[i] = H->Data[i / 2];
}
H->Data[i] = Huff;
}
//利用 堆 建立 HuffmanTree
HuffmanTree Huffman(MinHeap H)
{
HuffmanTree Huff;
int n = H->Size;
for (int i = 1; i < n; i++)
{
Huff = CreateHuffmanTree(); //初始化 空的 Huffman 树
Huff->Left = DeleteMin(H);
Huff->Right = DeleteMin(H);
Huff->weight = Huff->Left->weight + Huff->Right->weight;
InsertHeap(H, Huff);
}
Huff = DeleteMin(H);
//cout<<"ceshi:"<< Huff->weight << endl;
return Huff;
}
/*输入*/
map<char, int> mapp;
//调整最小堆
void sortHeap(MinHeap H, int i)
{
HuffmanTree Huff = H->Data[i];
int parent, child;
for (parent = i; 2 * parent <= H->Size; parent = child)
{
child = parent * 2;
// 如果右儿子更小
if ((child != H->Size) && (H->Data[child + 1]->weight < H->Data[child]->weight))
child++;
//如果父节点 更小,结束循环
if (H->Data[parent]->weight <= H->Data[child]->weight)
break;
//否则,替换
H->Data[parent] = H->Data[child];
H->Data[child] = Huff;
}
//H->Data[parent] = Huff;
}
void adjust(MinHeap H)
{
// 从第一个有孩子结点的结点开始调整
for (int i = H->Size / 2; i > 0; i--)
{
sortHeap(H, i);
}
}
//初始化建堆。读取输入,并调整为最小堆
//MinHeap InitHeap(int n)
//{
// MinHeap H = CreateMinHeap();
// HuffmanTree Huff;
//
// char c;
// int f;
// for (int i = 0; i < n; i++)
// {
// cin >> c >> f;
// mapp.insert(pair<char,int>(c,f));
// Huff = CreateHuffmanTree();
// Huff->weight = f;
// H->Data[++H->Size] = Huff;
// }
// adjust(H);
// return H;
//}
int array1[10001];
int array2[10001];
HuffArray Harray[10001];
MinHeap InitHeap(int n)
{
MinHeap H = CreateMinHeap();
HuffmanTree Huff;
char c;
int f;
for (int i = 0; i < n; i++)
{
cin >> c >> f;
//mapp.insert(pair<char,int>(c,f));
Huff = CreateHuffmanTree();
Huff->weight = f;
array1[i] = f;
H->Data[++H->Size] = Huff;
}
//adjust(H);
return H;
}
/*比较、计算 */
//计算 huffmantree的编码长度 //递归。先传入根节点 及其 深度
int WPL(HuffmanTree Huff, int depth)
{
//如果是叶节点 , 返回编码长度
if (Huff->Left == NULL && Huff->Right == NULL)
return depth * Huff->weight;
else
return (WPL(Huff->Left, depth + 1) + WPL(Huff->Right, depth + 1));
}
//提交 并 比较
//排序
void sortarray(HuffArray A[], int n) //大 到 小
{
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (A[i].slen < A[j].slen)
{
HuffArray tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
}
}
// 1.wpl大小 2.没有二义性
void submit(int n, int codeLen)
{
HuffmanTree Huff = CreateHuffmanTree();
string s;
char c;
int sLen;
int flag = 1;
int wpl=0;
for (int i = 0; i < n; i++)
{
cin >> c >> s;
sLen = count(s.begin(), s.end(), '0') + count(s.begin(), s.end(), '1');
Harray[i].slen = sLen;
Harray[i].str = s;
//计算WPL
wpl += array1[i] * Harray[i].slen;
}
//排序
sortarray(Harray, n);
//判断 二义性
for (int i = 0; i < n - 1; i++)
{
for (int j = Harray[i].slen; j > 0; j--)
{
string stmp;
stmp.assign(Harray[i].str,0, j); //string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串
for (int k = i+1; k < n; k++)
{
if (stmp == Harray[k].str)
{
flag = 0;
//cout << "flag=0" << endl;
}
}
}
}
if ((wpl <= codeLen)&&(flag ==1))
cout << "Yes";
else
cout << "No";
}
int main()
{
int N, X;
cin >> N;
MinHeap H = InitHeap(N);
HuffmanTree Huff = CreateHuffmanTree();
Huff = Huffman(H);
int codeLen = WPL(Huff, 0);
//cout << "WPL:" << codeLen << endl;
cin >> X;
for (int i = 0; i < X - 1; i++)
{
submit(N, codeLen);
cout << endl;
}
submit(N, codeLen);
}