编程笔记 数据结构 第九十章
二叉搜索树
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stack>
using namespace std;
/*
日期:2018/4/17
题目:二叉搜索树
*/
typedef int Elemtype;
typedef struct BSTNode
{
int key;
Elemtype data;
struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
// 1 创建时插入建立二叉搜索树
void insertBSTree(BSTree &t,BSTree &p)
{
if (t == NULL)
{
t = p; // 根节点为空
}
else {
if (p->data >= t->data)
insertBSTree(t->rchild,p);
else
insertBSTree(t->lchild,p);
}
}
void createBSTree(BSTree &t)
{
Elemtype x;
BSTree p;
int n ;
cout<< "请输入N和待排序的数据"<< endl;
cin >> n;
for (int i = 0;i<n;i++)
{
cin >> x;
p = (BSTree)malloc(sizeof(BSTNode));
p->data = x;
p->key = i+1;
p->lchild = NULL;
p->rchild = NULL;
insertBSTree(t,p);
}
}
// 中序遍历
void inOrder(BSTree t)
{
if (t)
{
inOrder(t->lchild);
cout << t->data << " ";
inOrder(t->rchild);
}
}
// 查找
int searchBSTree(BSTree &t,Elemtype data)
{
if (t)
{
if (data == t->data)
return t->key;
else if (data > t->data)
searchBSTree(t->rchild,data);
else
searchBSTree(t->lchild,data);
}else
return 0;
}
// 动态树表:树结构不是一次生成的,而是在查找过程中,当树中不存在关键字等于
// 给定值得结点时再进行插入,新插入的结点一定是叶子结点。
int searchBSTree_dynamic(BSTree t,Elemtype data,BSTree &p,BSTree &f)
{
if (t)
{
if (t->data == data)
{
p = t;
return 1;
}
else
{
if (t->data > data)
searchBSTree_dynamic(t->lchild,data,p,t);
else
searchBSTree_dynamic(t->rchild,data,p,t);
}
}
else
{
p = f;
return 0;
}
}
void insertBSTree_dynamic(BSTree &t,Elemtype data)
{
BSTree p=NULL,f = NULL; // p指向查找到的结点,f为未找到是时的父节点
BSTree s;
if (!searchBSTree_dynamic(t,data,p,f))
{
s = (BSTree)malloc(sizeof(BSTNode));
s->data = data;
s->lchild = NULL;
s->rchild = NULL;
// 在树中没找到时即要插入结点
if (p == NULL)
{
t = s; // t为空二叉树,插入的叶子结点即为根节点的特殊情况
}
else
{
if (data > p->data )
p->rchild = s;
else
p->lchild = s; // 插入新的叶子结点
}
}
}
/*
删除节点分为三种情况:
1 p的左右子树均空,直接修改父节点指针
2 p有一个子树,让该子树成为父节点的左子树
3 p的两个子树均不为空
递归找到该结点,然后删除
*/
void deleteBST(BSTree &t,Elemtype data)
{
void deleteB(BSTree&);
if (t)
{
if (t->data == data)
{
deleteB(t);
}
else if (t->data > data)
deleteBST(t->lchild,data);
else
deleteBST(t->rchild,data);
}
}
int deleteB(BSTree &p)
{
BSTree q,s;
if (p->lchild == NULL &&p->rchild == NULL)
{
q = p;
p = NULL;
free(q);
}
else
if (p->lchild == NULL)
{ // 只有左子树空
q = p;
p = p->rchild;
free(q);
}
else // 只有右子树空
if (p->rchild == NULL)
{
q = p;
p = p->lchild;
free(q);
}
else // 均不空
{
s = p->lchild;
while (s->rchild!=NULL)
s = s->rchild;
s ->rchild = p->rchild;
q = p;
p = p->lchild;
free(q);
}
}
// delete 和 free 的的区别?
int main()
{
BSTree t = NULL;
//createBSTree(t);
int n;
Elemtype x;
cout << "n" << endl;
cin >> n ;
for (int i=0;i<n;i++)
{
cin >> x;
insertBSTree_dynamic(t,x);
}
inOrder(t);
cout << "请输入要删除的数据" <<endl;
cin >> x;
deleteBST(t,x);
inOrder(t);
return 0;
}
二叉搜索树的复习
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stack>
using namespace std;
/* 二叉搜索树 复习
1. 创建+插入
2. 搜索
3. 删除
4. 遍历
*/
typedef struct BSTNode
{
int key;
struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
int insertBSTree(BSTree &t,BSTree &p)
{
// 如果找到则返回指向关键字的指针,否则插入到树中
if (t== NULL)
{
t = p;
return 1;
}else
{
if (t->key == p->key)
{
return 1;
}else if (t->key > p->key)
insertBSTree(t->lchild,p);
else
insertBSTree(t->rchild,p);
}
}
void createBSTree(BSTree &t)
{
t = NULL;
int n;
cout << "请输入N:" <<endl;
cin >> n;
cout << "请输入N个数" << endl;
int key;
for (int i=0;i<n;i++)
{
cin >> key;
BSTree p = (BSTree)malloc(sizeof(BSTNode));
p->key = key;
p->lchild = NULL;
p->rchild = NULL;
insertBSTree(t,p);
}
}
void preOrder(BSTree T)
{
if (T)
{
preOrder(T->lchild);
cout << T->key << " ";
preOrder(T->rchild);
}
}
void deleteT(BSTree &p)
{
BSTree q;
// 删除P指向的结点
if (p->lchild == NULL && p->rchild == NULL )
{
q = p;
p = NULL;
free(q);
return ;
}
if (p->lchild == NULL)
{
q = p;
p = p->rchild;
free(q); // free 和 delete 的区别
// malloc - free C new - delete C++
}
else if (p->rchild == NULL)
{
q = p;
p = p ->lchild;
free(q);
}
else
{
// 左右子树均不为空时,P左子树的最右侧的结点+P的右子树
BSTree s = p->lchild;
while (s->rchild != NULL)
s = s->rchild;
s->rchild = p->rchild;
q = p;
p = p->lchild;
free(q);
}
}
void deleteBSTNode(BSTree &t,int key)
{
if (t)
{
// t为空时则为未找到
if (t->key == key)
deleteT(t);
else if (t->key > key)
deleteBSTNode(t->lchild,key);
else
deleteBSTNode(t->rchild,key);
}
}
int main()
{
BSTree t;
createBSTree(t);
preOrder(t);
cout << "key:" << endl;
int key;
cin >> key;
deleteBSTNode(t,key);
preOrder(t);
return 0;
}
哈希表的实现
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stack>
#define SUCCESS 1
#define UNSUCCESS -1
#define NULLkey -65519
using namespace std;
/*
哈希表
*/
const int HashLength = 20;
typedef int Elemtype;
typedef struct
{
Elemtype *elem;
int count; // 当前元素个数
}HashTable;
void initHashTable(HashTable &h)
{
h.elem = (Elemtype *)malloc(sizeof(Elemtype)*HashLength);
h.count = 0;
for (int i = 0;i<HashLength;i++)
h.elem[i] = NULLkey;
}
int hash_f(int key)
{
return key%HashLength;
}
void insertHashTable(HashTable &h,int key)
{
int addr = hash_f(key);
while (h.elem[addr]!= NULLkey)
{
addr = (addr+1)%HashLength;
}
h.elem[addr] = key;
h.count++;
}
int searchHashTable(HashTable h,int key)
{
// 查找成功返回存储位置否则返回UNSUCCESS
int addr = hash_f(key);
while (h.elem[addr]!=key)
{
addr = (addr+1)%HashLength;
if (h.elem[addr] == NULLkey || addr == hash_f(key))
return UNSUCCESS;
// 如果线性探测再散列到NULLKEY或者转了一圈重新回到起点则没找到
}
return addr;
}
int main()
{
int a[] = {14,1,68,27,55,19,20,84,79,23,11,10};
HashTable h;
initHashTable(h);
for (int i = 0;i<12;i++ )
insertHashTable(h,a[i]);
for (int i=0;i<12;i++)
{
int addr = searchHashTable(h,a[i]);
cout << a[i] << " : " << addr << endl;
}
return 0;
}
排序
分类:
1 插入类
- 直接插入
- 折半插入
- 希尔排序
2 交换类 - 冒泡排序
- 快速排序
3 选择排序 - 简单选择排序
- 堆排序
4 归并类 - 归并排序
5 基数排序
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stack>
#define SUCCESS 1
#define UNSUCCESS -1
#define NULLkey = -65519
using namespace std;
/*
排序
*/
// 直接插入排序:每次都是在一个有序的序列中插入一个新的关键字
void insertSort(int r[],int n) // 待排序关键字存储在1-n的位置
{
/*
时间复杂度:
1 最好情况:仅有外层循环 O(n)
2 最坏情况:n*(n-1)/2 O(n^2)
空间复杂度:
辅助存储空间仅有temp,故为O(1)
*/
int temp;
for (int i=1;i<=n;i++)
{
temp = r[i];// 存放待插入关键字
int j = i-1;
while (r[j] > temp && j > 0)
{
r[j+1] = r[j];
j--;
}
r[j+1] = temp;
}
}
void binsertSort(int r[],int n)
{
/*
和直接插入排序的区别是:查找插入位置所花的时间大大减小
*/
for (int i=1;i<=n;i++) // i = 1 时已经为有序数列
{
r[0] = r[i] ; // r[0] 空余空间中存放当前待插入关键字
int low = 1,high = i-1; // 当前的有序序列为1-i-1;
while (low <= high)
{
int mid = (low + high)/2;
if (r[mid] >= r[0])
{
high = mid-1;
}
else
low = mid+1;
}
// 之所以让low = high时再进入循环:比较当前关键字和high/low指向关键字的大小
// 离开循环时low = high+1,low指向位置即为待移动的位置
for (int j=i-1;j>=low;j--)
r[j+1] = r[j];
r[low] = r[0];
}
}
// 希尔排序
void shellInsert(int r[],int n,int dlta)
{
int j;
for (int i = 1+dlta; i<=n ; i++)
{ // 循环 n - 1 - dlta 次 = 子序列的个数
if (r[i] < r[i-dlta])
{
r[0] = r[i];
for (j = i-dlta ; j>0 && r[j] > r[0] ; j = j-dlta )
r[j+dlta] = r[j];
r[j+dlta] = r[0];
}
}
}
void shellSort(int r[],int dlta[],int n,int t )
{
/*
缩小增量排序:对r[n]进行增量为dlta[t]的排序过程
思想:直接插入排序适合基本有序的序列,希尔排序的每趟排序都会使整个序列变得更加有序,
等整个序列基本有序后,最后进行一趟直接插入排序,效率会更高。
注意:1.希尔排序是不稳定的。
2.增量序列的最后一个值一定取1,增量序列中的值尽量没有除1之外的公因子
*/
for (int i = 0;i<t;i++)
{
shellInsert(r,n,dlta[i]);
}
}
void bubbleSort(int r[],int n)
{
/*
外层比较n-1次,内层比较n-i次
时间复杂度:O(N^2)
空间复杂度:O(1)
*/
int flag;
for (int i = 1;i<n;i++)
{
flag = true;
for (int j = 1;j<=n-i;j++)
{
if (r[j] > r[j+1])
{
int temp = r[j];
r[j] = r[j+1];
r[j+1] = temp;
flag = false;
}
}
if (flag) // 没发生交换说明有序
break;
}
}
// 快速排序之合并版
/*
通常,快速排序是同量级O(nlogn)的排序方法中,平均性能最好。
但若初始记录序列按关键字有序或基本有序时,快速排序将退化为起泡排序。
空间复杂度:栈
*/
void quickSort(int r[],int low,int high)
{
int i = low,j = high;
if (low < high)
{
while (i<j)
{
r[0] = r[low];//枢纽
while (r[j]>r[0] && i < j)
j -- ;
if (i < j)
{
// 此时r[j] < r[0] 交换
r[i] = r[j];
i++;
}
while (i < j && r[i] < r[0] )
i++;
if (i<j)
{
r[j] = r[i];
j--;
}
}
r[i] = r[0];// 将枢纽定在最终位置
quickSort(r,low,i-1);
quickSort(r,j+1,high);
}
}
// 快速排序之分离版
int Partition(int r[],int low,int high)
{
while (low<high)
{
r[0] = r[low];
while (low < high && r[high] > r[0] )
high -- ;
if (low < high)
{
r[low] = r[high];
low++;
}
while (low<high && r[low] < r[0])
low++;
if (low <high)
{
r[high] = r[low];
high--;
}
}
r[low] = r[0];
return r[0];
}
void qsort(int r[],int low,int high)
{
if (low < high)
{
int flag = Partition(r,low,high); // 一分为二并得到枢纽
qsort(r,low,flag -1);
qsort(r,flag+1,high);
}
}
void print(int r[],int n)
{
for (int i=1;i<=n;i++)
cout << r[i] <<' ';
cout << endl;
}
// 简单选择排序
void selectSort(int r[],int n)
{
int i,j,k;
for (i=1;i<=n-1;i++)
{
k = i;
for (j=i;j<=n;j++)
{
if (r[j]<r[k])
{
k = j;
}
}
if (k!=i)
{
r[0] = r[i];
r[i] = r[k];
r[k] = r[0];
}
}
}
/*
堆排序:定义:非叶子结点的值都不大于(或不小于)其左右孩子结点的值。
将无序序列调整为完全二叉树形式的堆,根节点为最大值或最小值。
通过堆找到最大最小值后放在队列的尾部,将无序队列调整为有序队列。
执行过程:
1 层次遍历法建立初始堆,找到第一个非叶子结点,按照从右往左,从下往上的顺序,
依次判断是否满足堆定义(假设为大顶堆,即孩子结点的数值是否都小于根节点)。
如不满足则选择孩子中较大的结点和根节点交换,同时判断下移的根节点的值是否满足定义。
2 将当前无序序列的第一个值和最后一个值交换,有序序列中增加一个值。判断和根节点交换的
新节点是否满足堆定义并进行调整。
时间复杂度:
*/
void shift(int r[],int low,int high)
{
int temp = r[low];
int i = low,j = 2*i;
while (j<=high)
{
if (j<high && r[j] < r[j+1])
{
j = j+1; // 选择I节点的孩子中较大的一个
}
if (temp < r[j])
{
r[i] = r[j];
i = j;
j = j*2;
}
else
break;
}
r[i] = temp;
}
void heapSort(int r[],int n)
{
// 1. 找到最后一个非终端节点,从右向左,从下向上进行调整,建立初始堆
int i;
for (i = n/2; i >=1 ;i--)
{
shift(r,i,n);
}
// 2. 将一次调整后的最大值和无序序列的最后一个值交换
for (i = n;i>=2;i--) // 进行n-1次排序
{
r[0] = r[1];
r[1] = r[i];// r[i]为最后一个
r[i] = r[0];
shift(r,1,i-1);//在减小了一个关键字的无序序列中进行调整
}
}
int main()
{
int n ;
cin >> n;
int r[n];
for (int i=1;i<=n;i++)
cin >>r[i];
//insertSort(r,n);
//binsertSort(r,n);
// bubbleSort(r,n);
//quickSort(r,1,n);
// selectSort(r,n);
heapSort(r,n);
print(r,n);
return 0;
}