数据结构/数据结构与算法实验四 二叉排序树与快速排序(查找与排序算法的实现)

简介: 数据结构/数据结构与算法实验四 二叉排序树与快速排序(查找与排序算法的实现)

1.实验题目

1.已知二叉树T的结点形式为(lchild、data、count、rchild),其中count为查找次数计数。在树中查找值为X的结点,若找到则该结点的count加1,函数返回值为TRUE;否则,作为一个新结点插入树中,插入后仍为二叉排序树,且函数返回值为FALSE。写出其非递归算法(迭代算法)。(教材P310,习题八的“四、应用题”的第10题。)

2.验证快速排序的递归算法。

3.利用快速排序的思想编写算法,对关键字取值为整数的一个数据元素序列进行整理,使所有关键字为负值的数据元素排在关键字为非负值的数据元素之前。

2.实验要求

查找实验要求实现以下功能:

1.对从键盘输入的顺序任意的若干个正整数建立一棵二叉排序树,以-1作为结束。

2.先给出二叉排序树的先序、中序和后序遍历结果,然后从键盘输入一个整数,在二叉排序树中删除该整数,并给出是否删除成功的结果,若删除成功,则再给出删除后的二叉排序树的先序、中序和后序遍历结果。

3.先给出二叉排序树的先序、中序和后序遍历结果,然后从键盘输入一个整数,调用实现“二、实验题目”的功能。根据函数调用结果,显示查找成功的该结点的count值,或者是给出插入新的结点后的二叉排序树的先序、中序和后序遍历结果。

排序实验要求:

1.对从键盘输入的任意个整数,通过递归快速排序使之成为有序的序列,输出每一趟排序的结果。

2.对从键盘输入的任意个整数,按照“1.实验题目”的第1小题的要求输出结果。

3.算法思路

1.建立二叉排序树

在主程序中运用while语句输入一行数据,以-1为终止。输入时,调用二叉排序树的插入函数。插入函数的逻辑如下:先查找该数,若找到则插入失败。若未找到则插入。插入时,若该树为空树,则创建根结点。若该树不为空树,则在相应位置创建含有所需数据的结点。

2.遍历二叉排序树

先序、中序、后序遍历二叉树。先序遍历二叉排序树时,先访问当前结点,再先序遍历左子树,最后先序遍历右子树。中序遍历二叉排序树时,先中序遍历左子树,再访问根结点,最后中序遍历右子树。后序遍历二叉排序树时,先后序遍历左子树,再后序遍历右子树,最后访问根结点。

3.删除节点

先查找要删除的点p。若查找成功,则调用删除算法,删除成功。若查找失败,则删除也失败。删除时,从根结点开始重新查找该结点。若p的数据比当前访问的结点的数据小,则在左子树上删除p点。若p的数据比当前访问的结点的数据大,则在右子树上删除p点。若找到了p结点,则判断其有没有左子树或右子树。若其没有左子树也没有右子树,则是个叶结点,直接删除。若只有右子树但没有左子树,则将点p替换成右子树上第一个结点。若其只有左子树但没有右子树,则将p替换成左子树上第一个结点。若其不仅有右子树而且有左子树,则找到其右子树上最左的结点s,将s替换p,并删掉s结点。

4.查找并记录访问次数

在设计类时,结点除了数据域、指针域,还有一个count域记录结点被访问的次数。利用迭代法,通过循环的终止条件控制是否找到p点。若没找到p点,则调用插入结点的算法。若找到p点,则count增加1。

5.利用快速排序的思想将负数排在正数前

在快速排序的算法中,我们将数组第一个元素作为基准,将数组划分成比基准小、比基准大两个部分。在这个算法中,我们以0而非数组第一个元素作为基准元素,便可以将比0小的数排在比0大的数之前。我们仍然用e=elem[low]这一语句记录数组第一个元素,但这个元素不是基准,而是在i和j指针“撞”在一起的位置上将这个元素放回。无论第一个元素正负,结果上都会是前面是负数,后面是非负数。

4.输出演示

数据的输入

先序遍历

中序遍历

后序遍历

删除数据(删除失败)

删除数据(删除成功)

删除成功后的遍历序列

查找成功

查找失败,插入成功

5.源代码

题目1

binarySortTree.h

#pragma once
#include"binTreeNode.h"
#include<Windows.h>
#include<iostream>
using namespace std;
template <class elemtype>
class binarySortTree
{
private:
  binTreeNode<elemtype>* root;
public:
  bool isEmpty() {//判空
    return root == NULL;
  }
  binTreeNode<elemtype>* find(const elemtype& key, binTreeNode<elemtype>*& f) const{
    //从f结点开始向下查找值为key的结点
    binTreeNode<elemtype>* p=root;
    f = NULL;//初始化
    while (p != NULL && p->data != key) {//往下搜索
      if (key < p->data) {
        f = p;
        p = p->lchild;
      }
      else {
        f = p;
        p = p->rchild;
      }
    }
    return p;//若数值不存在,则指向NULL结点被返回
  }
  void preOrder(binTreeNode<elemtype>* r) const {//先序遍历
    if (r != NULL) {
      cout << r->getData()<<" ";
      preOrder(r->lchild);
      preOrder(r->rchild);
    }
  }
  void inOrder(binTreeNode<elemtype>* r) const {//中序遍历
    if (r != NULL) {
      inOrder(r->lchild);
      cout << r->getData()<<" ";
      inOrder(r->rchild);
    }
  }
  void postOrder(binTreeNode<elemtype>* r) const {//后序遍历
    if (r != NULL) {
      postOrder(r->lchild);
      postOrder(r->rchild);
      cout << r->getData()<<" ";
    }
  }
  bool append(const elemtype& e) {
    //为平衡二叉树添加一个新的结点
    binTreeNode<elemtype>* f=NULL;//指向被查找结点双亲
    if (find(e, f) == NULL) {//查找失败,插入成功
      binTreeNode<elemtype>* p;
      p = new binTreeNode<elemtype>(e);
      if (isEmpty())//若树是空树
        root = p;
      else if (e < f->data)//若不是空树,则插入到相应位置上
        f->lchild = p;
      else
        f->rchild = p;
      return true;
    }
    else
      return false;//查找成功,不能插入数值重复的结点,插入失败
  }
  void Delete(const elemtype& k, binTreeNode<elemtype>*& p) {
    //在p为根的二叉排序树上删除关键字为k的结点
    binTreeNode<elemtype>* s, * temp;
    if (p != NULL)
      if (k < p->data)//还没找到p
        Delete(k, p->lchild);//递归地在p的左子树上删除关键字为k的结点
      else if (k > p->data)
        Delete(k, p->rchild);//递归地在p的左子树上删除关键字为k的结点
      else if (p->lchild != NULL && p->rchild != NULL){//找到p,但是p的左右子树都不空 
        //s = Min(p->rchild);
        temp = p->rchild;
        while (temp->lchild != NULL) {
          temp = temp->lchild;
        }//找到p的右子树上最小的数s,替换掉p,然后删掉s
        s = temp;
        p->data = s->data;//将p替换成s
        Delete(s->data, p->rchild);//递归地删掉s
      }
      else {//相等找到,但是左或右为空
        temp = p;
        if (p->lchild == NULL)       p = p->rchild;//左子树空,则将p替换为右子树上第一个结点
        else if (p->rchild == NULL)  p = p->lchild;//右子树空,则将p替换为左子树上第一个结点
        delete temp;
      }
  }
  bool t2(int val){//第二题:先查找,查找成功则删除成功,查找失败则删除失败
    binTreeNode<elemtype>* f = NULL;
    binTreeNode<elemtype>* p = find(val, f);
    if (p == NULL) {
      //查找失败,删除失败
      cout << "删除失败!";
      return false;
    }
    else {
      //查找成功,删除成功
      //deleteNode(p);
      Delete(val, root);
      cout << "\n删除成功!";
      return true;
    }
  }
  bool t3(int key) {//第三题:先查找,查找成功则计数,查找失败则插入
    binTreeNode<elemtype>* p=root,*f = root;
    f = NULL;//f为要找的结点的双亲结点
    while (p != NULL && p->data != key) {//利用迭代法查找
      if (key < p->data) {
        f = p;
        p = p->lchild;
      }
      else {
        f = p;
        p = p->rchild;
      }
    }
    if (p != NULL) {//查找成功
      p->count++;//访问到了p结点,则p的被访问次数需要计数
      cout << "查找成功!";
      return true;
    }
    else {//查找到最后查找失败时,若数据比双亲结点的数小,则说明双亲结点左子树为空
      //若数据比双亲结点的数大,则说明双亲结点右子树为空
      cout << "查找失败!正在插入该结点";
      if(root==NULL)
        root= new binTreeNode<elemtype>(key);//若为空树,则需要创建一个根结点
      else {//在相应位置上创建新的结点,并且查找失败
        if (key > f->data)
          f->rchild = new binTreeNode<elemtype>(key);
        else
          f->lchild = new binTreeNode<elemtype>(key);
      }
      return false;
    }
  }
  binTreeNode<elemtype>* getRoot() {
    return this->root;
  }
  binarySortTree() {
    root = NULL;//默认的无参构造函数
  }
};

binTreeNode.h

#pragma once
#include<Windows.h>
template<class elemtype>
struct binTreeNode
{
  elemtype data;
  int count;//结点被访问次数
  binTreeNode<elemtype>* lchild;
  binTreeNode<elemtype>* rchild;
  binTreeNode(elemtype mdata = 0) {//构造函数,接受无参数构造
    count = 0;
    data = mdata;
    lchild = NULL;
    rchild = NULL;
  }
  int getcount() {
    return count;
  }
  elemtype getData() {
    return data;
  }
};

main.cpp

#include<iostream>
#include"binarySortTree.h"
using namespace std;
int main() {
  int num;//利用num变量输入数字
  int fun;//选择功能
  int val;//删除或插入数据时输入
  binarySortTree<int> bintree;
  cout << "请输入数据,以-1为终止";
  cin >> num;
  while (num != -1) {//输入数据,以-1为终止
    bintree.append(num);
    cin >> num;
  }
  binTreeNode<int>* p=NULL;//工作指针
  binTreeNode<int>* root=bintree.getRoot(),*mroot = bintree.getRoot();//根结点
  do{
    cout << "\n请选择你想要使用的功能:\n";
    cout << "1.先序遍历\n2.中序遍历\n3.后序遍历\n4.删除数据\n5.查找数据\n6.退出";
    cin >> fun;
    switch (fun) {
    case 1:
      cout << "先序遍历序列为:";
      bintree.preOrder(root);
      break;
    case 2:
      cout << "\n中序遍历序列为:";
      bintree.inOrder(root);
      break;
    case 3:
      cout << "\n后序遍历序列为:";
      bintree.postOrder(root);
      break;
    case 4:
      cout << "\n请输入你想删除的数字:";
      cin >> val;
      bintree.t2(val);
      break;
    case 5:
      cout << "\n请输入你要插入或查找的数字:";
      cin >> val;
      if (bintree.t3(val)) {//调用题3中用迭代实现的函数
        mroot = root;
        p=bintree.find(val,mroot);//find函数中的参数是取引用调用,所以根结点地址需要在调用函数前重置
        cout <<"这个节点已被访问次数"<< p->getcount();//函数只要返回查找成功或失败而不自带输出访问次数的功能
      }
      else {
        cout << "自动显示遍历结果!";
        cout << "\n先序遍历序列为:";
        bintree.preOrder(root);
        cout << "\n中序遍历序列为:";
        bintree.inOrder(root);
        cout << "\n后序遍历序列为:";
        bintree.postOrder(root);
      }
      break;
    default:break;
    }
  } while (fun != 6);//输入6以退出
  cout << "\n运行完成!";

第二题

main.cpp

#include<iostream>
#include<stdio.h>
using namespace std;
int times = 0;//全局变量,记录是第几次排序
int n = 0;//全局变量,记录数组长度
template<class elemtype>
void quicksort(elemtype elem[], int low, int high) {
  elemtype e = elem[low];
  int i = low, j = high;
  while (i < j) {
    while (i < j && elem[j] >= e)j--;
    if (i < j) elem[i++] = elem[j];
    while (i < j && elem[i] <= e) i++;
    if (i < j) elem[j--] = elem[i];
  }
  elem[i] = e;
  times++;
  printf("第%d次排列的结果为:", times);
  for (int t = 0; t < n; t++)
    cout << elem[t] << " ";
  cout << endl;//在递归进入下一层排序前先输出本次排序结果
  if (low < i - 1) quicksort(elem, low, i - 1);
  if (i + 1 < high)quicksort(elem, i + 1, high);
}
template<class elemtype>
void findNegative(elemtype elem[], int n) {
  elemtype e = elem[0];
  int i = 0, j =n-1;
  while (i < j) {
    while (i < j && elem[j] >= 0)j--;
    if (i < j) elem[i++] = elem[j];
    while (i < j && elem[i] <0) i++;
    if (i < j) elem[j--] = elem[i];
  }//寻找负数的思想与快速排序类似
  //快速排序中将数字与第一个数比
  //这里将数字与0比
  //最后无论第一个数的大小都要将第一个数放到对应位置。
  elem[i] = e;
  //算法已经结束,没有递归的后续过程
}
int main() {
  cout << "请输入数组中数的个数:\n";
  cin >> n;
  int *arr=new int[n];
  cout << "请输入数据:";
  for (int i = 0; i < n; i++)
    cin >> arr[i];
  int func,low,high;
  cout << "请选择功能:";
  cout << "1.快速排序";
  cout << "2.将负数整理在非负数前";
  cin >> func;
  switch (func) {
    case 1:
      low = 0;
      high = n - 1;
      quicksort(arr, low, high);
      break;
    case 2:
      findNegative(arr, n);
      break;
  }
  for (int i = 0; i < n; i++)
    cout << arr[i]<<" ";
}


目录
相关文章
|
1月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
69 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
30天前
|
存储 算法 Java
Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性
Java Set因其“无重复”特性在集合框架中独树一帜。本文解析了Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性,并提供了最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的hashCode()与equals()方法。
32 4
|
1月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
105 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
1月前
|
搜索推荐 算法
数据结构与算法学习十四:常用排序算法总结和对比
关于常用排序算法的总结和对比,包括稳定性、内排序、外排序、时间复杂度和空间复杂度等术语的解释。
19 0
数据结构与算法学习十四:常用排序算法总结和对比
|
1月前
|
机器学习/深度学习 搜索推荐 算法
探索数据结构:初入算法之经典排序算法
探索数据结构:初入算法之经典排序算法
|
1月前
|
算法 Java 索引
数据结构与算法学习十五:常用查找算法介绍,线性排序、二分查找(折半查找)算法、差值查找算法、斐波那契(黄金分割法)查找算法
四种常用的查找算法:顺序查找、二分查找(折半查找)、插值查找和斐波那契查找,并提供了Java语言的实现代码和测试结果。
19 0
|
15天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
6天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
15 1
|
9天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
12天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。