减治法实现插入排序,减治法实现二叉查找树(二叉搜索数,二叉排序数)的创建、插入与查找(含解析与代码实现)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 减治法实现插入排序,减治法实现二叉查找树(二叉搜索数,二叉排序数)的创建、插入与查找(含解析与代码实现)

正文


减治法


⚫ 减治技术:利用一个问题给定实例的解与同样问题较小实例的解之间的某种关系,可将原问题从顶至下,或从底至上地解决。从顶至下导致递归算法,从底至上导致增量法(注:从求解问题的一个较小实例开始,迭代实现,往往要好于递归)。


⚫减治法的三种变化形式:减去一个常量、减去一个常量因子、减去的规模可变。


       减常量:每次迭代总是从实例中减去一个相同的常量(一般来说,这个常量为1)。

               例如,计算a"的值,f(n)=a"


                乘法为基本操作,上述减常量算法时间效率为O(n)。

433.png

 

       减常因子:每次迭代总是从实例的规模中减去一个相同的常数因子(一般来说,这个常数因子为2)。


               和上述同样的例子,计算a"的值,f(n)=a"

               乘法为基本操作,上述减常因子算法效率为O(log n)。

432.png

   减可变规模: 算法在每次迭代时,规模减小的模式可变。例如,欧几里得算法:

gcd(m,n) = gcd(n,m mod n)


插入排序


       减治法最典型的案例之一便是插入排序。

       插入排序思想:假设较小数组A[O..n-2]已排序好,为了得到一个大小为n的有序数组,在A[0..n-2]中找到一个合适位置,将A[n-1]插入该位置。迭代实现的插入排序伪代码如下:

InsertionSort(A[O..n-1])
//用插入排序对给定数组排序
//输入:n个可排序元素构成的一个数组A[0..n一1]
//输出:非降序排列的数组A[0..n一1]
for i <- 1 to n-1 do
        v <- A[i]
        j <- i-1
        while j ≥ 0 and A[j] > v  do
                A[j + 1] <- A[j]
                j <- j-1
                A[j + 1] <- v

 例子:对数组89,45,68,90,29,34,17排序:


插入排序

       代码实现:

#include<stdio.h>
#include<stdlib.h>
void insort(int *a,int n)
{
  printf("排序前为:");
  for(int i = 0; i < n;i++)
    printf("%d ",*(a + i));
  }
  for(int i = 1; i < n;i++)
  {
    for(int j = i-1;j >= 0;j--)
    {
      if(*(a+j+1) < *(a+j))   //如果前面的数比后面大,就交换
      {
        int t = *(a+j+1);
        *(a+j+1) = *(a+j); 
        *(a+j) = t;
      }
    }
    printf("\n排序中:");     //显示每循环一次之后的排序结果
    for(int i = 0; i < n;i++)
    {
      printf("%d ",*(a + i));
    }
  }
  printf("\n排序后为:");
  for(int i = 0; i < n;i++)
  {
    printf("%d ",*(a + i));
  }
  printf("\n");
}
int main(int argc,char * argv[])
{
  int *str = NULL;  //存储要排序的数组
  int n;        //存储排序数组个数
  printf("请输入要排序的个数:");
  scanf("%d",&n);
  str = (int *)malloc(n * sizeof(int)); //获取个数后,给str分配空间
  printf("请输入要排序的数据(以空格隔开):");
  for(int i = 0; i < n;i++)       //循环将数据写入str中
  {
    scanf("%d",(str + i));
  }
  insort( str , n);   //调用排序算法
}


二叉查找数


       二叉树的节点包含排序项集合的元素,每个节点一个元素,对于每个节点,其所有左子树的元素都小于这个节点,其所有右子树的元素都大于这个节点。

       二叉查找树查找一个键值v的过程:将v与树的根节点进行比较,若相等,直接返回,若小于,在左子树中继续查找,若大于,在右子树中继续查找。

       算法的每次迭代,都简化为在一棵更小的二叉查找子树中查找(注:由于子树的高度是不一样的,因此,每次减少的规模可能是不一样的)。


       在二叉查找树中查找与插入键具有相等的时间效率。最坏时间效率为0(n)(例如:当连续插入的键是一个递增或递减序列时),平均时间效率为0(log n)。


       创建

       算法思路:


二叉排序树的创建过程,实际上就是把一个结点加入到一颗二叉排序数中,并且保证其二叉排序性过程。

       不断重复二叉排序树的插入操作。

       二叉排序树的插入操作:

               1.找插入位置

               2.执行插入操作

/* 
create_sotr_tree:创建一颗二叉排序树
  返回值:
    二叉排序树的根节点指针
 */
BiTNode *create_sort_tree()
{
    //保存根结点的指针
    BiTNode *r = NULL;
    //step1 从键盘上获取数据
    int i = 0;
    char str[64] = {0};
    //gets(str);
  /*for(int i = 0;i<64;i++)
  {
    scanf("%s",&str[i]);
    if(str[i] == '0')
      break;
  }*/
  scanf("%s",str);
    while(str[i])
    {
        //step2 创建新的数据结点 并且把数据写进去
        BiTNode *pnew = malloc(sizeof(*pnew));
        pnew->data = str[i];
        pnew->lchild = NULL;
        pnew->rchild = NULL;
        r = inset_node(r,pnew);
        i++;
    }
    return r;
}


   插入与删除节点

       插入与删除都有多种情况需要考虑


插入


       二叉数是空数


       遍历找到应该挂的位置,子树为空时挂上去


删除


       删除的是叶子节点,直接删除


       删除的节点有右子树


       删除的节点有左子树


       删除的节点有左右子树

//在一棵二叉排序树中增加结点
BiTNode *inset_node(BiTNode *r, BiTNode *pnew)
{
    BiTNode *p = r;//遍历指针 用来查找挂结点的位置
    if(r == NULL)//从无到有  这棵树是一颗空树 那么新插入的结点就是数本身
    {
        return pnew;
    }
    if(pnew == NULL)
    {
        return r;
    }
    while(1)
    {
        if(pnew->data < p->data)
        {
            if(p->lchild == NULL)//如果p左孩子为空 直接挂上去
            {
                p->lchild = pnew;
                break;
            }
            p = p->lchild;
        }
        else if(pnew->data > p->data)
        {
            if(p->rchild == NULL)//如果p右孩子为空 直接挂上去
            {
                p->rchild = pnew;
                break;
            }
            p = p->rchild;
        }
        else
        {
            printf("data repeated\n");
            break;
        }
    }
    return r;
}
BiTNode *delete_node(BiTNode *r,u4 x)
{
  //step1 找到要删除的节点px,以及他的父节点pf
  BiTNode *px = r;
  BiTNode *pf = NULL;
  while(px)
  {
    if(px->data == x+48)
    {
      break;
    }
    else if(px->data > x)
    {
      pf = px;  //确保pf指向px 的父节点
      px = px->lchild;
    }
    else  //px->data < x
    {
      pf = px;
      px = px->rchild;
    }
  }
  if(px == NULL)
  {
    return r;
  }
  //分情况删除
  if(px == r && px->lchild == NULL && px->rchild == NULL) //根节点且左右节点都为空
  {
    free(r);
    printf("删除节点后,树为空!\n");
    return NULL;
  }
  else if(px == r && px->lchild == NULL)  //根节点且左节点为空
  {
    r = r->rchild;
    free(px);
  }
  else if(px == r && px->rchild == NULL)  //根节点且右节点为空
  {
    r = r->lchild;
    free(px);
  }
  else if(px->lchild == NULL && px->rchild == NULL) //为叶子节点
  {
    if(pf->lchild == px)
    {
      pf->lchild = NULL;
      free(px);
    }
    else
    {
      pf->rchild = NULL;
      free(px);
    }
  }
  else if(px->lchild != NULL && px->rchild == NULL) //只有右孩子
  {
    if(pf->lchild == px)
    {
      pf->lchild = px->lchild;
      px->lchild = NULL;
      free(px);
    }
    else
    {
      pf->rchild = px->lchild;
      px->lchild = NULL;
      free(px);
    }
  }
  else if(px->lchild == NULL && px->rchild != NULL) //只有左孩子
  {
    if(pf->lchild == px)
    {
      pf->lchild = px->rchild;
      px->rchild = NULL;
      free(px);
    }
    else
    {
      pf->rchild = px->rchild;
      px->rchild = NULL;
      free(px);
    }
  }
  else  //px->lchild != NULL && px->rchild != NULL  //有两个度的节点
  {
    BiTNode *p = px;
    BiTNode *pre = NULL;
    p = p->rchild;
    while(p->lchild)
    {
      pre = p;
      p = p->lchild;
    }
    px->data = p->data;
    pre->lchild = NULL;
    free(p);
  }
  return r;
}

  查找

/*在一棵二叉排序树中查找节点值
  参数:
    @BiTNode *r:传入的二叉排序数
    @int n:要查找的数
  返回值:
    char *:返回这个值在树中的顺序(如“01101”,左0右1)
*/
char *Find_node(BiTNode *r, int n)
{
    BiTNode *p = r;     //遍历指针 用来查找结点的位置
    char *str = NULL;   //用来记录查找时的顺序
  str = (char *)malloc(sizeof(char)*128);
    if(r == NULL)     //这棵树是一颗空树,直接返回
    {
        return str;
    }
    for(int i = 0; ;i++)
    {
        if(n < p->data)   //n小于当前节点值,往左走,str中记0
        {
      sscanf("0",(str+i));
            if(p->lchild == NULL)//如果p左孩子为空 直接挂上去
            {
                printf("二叉排序树中没有这个树\n");
        str = NULL;
                break;
            }
            p = p->lchild;
        }
        else if(n > p->data)  //n小于当前节点值,往右走,str中记1
        {
      sscanf("1",(str+i));
            if(p->rchild == NULL)//如果p右孩子为空 直接挂上去
            {
                printf("二叉排序树中没有这个树\n");
        str = NULL;
                break;
            }
            p = p->rchild;
        }
        else
        {
            break;
        }
    }
    return str;
}
相关文章
|
15天前
|
机器学习/深度学习 搜索推荐 API
淘宝/天猫按图搜索(拍立淘)API的深度解析与应用实践
在数字化时代,电商行业迅速发展,个性化、便捷性和高效性成为消费者新需求。淘宝/天猫推出的拍立淘API,利用图像识别技术,提供精准的购物搜索体验。本文深入探讨其原理、优势、应用场景及实现方法,助力电商技术和用户体验提升。
|
17天前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
18 1
|
22天前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
46 2
|
25天前
|
机器学习/深度学习 存储 人工智能
强化学习与深度强化学习:深入解析与代码实现
本书《强化学习与深度强化学习:深入解析与代码实现》系统地介绍了强化学习的基本概念、经典算法及其在深度学习框架下的应用。从强化学习的基础理论出发,逐步深入到Q学习、SARSA等经典算法,再到DQN、Actor-Critic等深度强化学习方法,结合Python代码示例,帮助读者理解并实践这些先进的算法。书中还探讨了强化学习在无人驾驶、游戏AI等领域的应用及面临的挑战,为读者提供了丰富的理论知识和实战经验。
50 5
|
1月前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
128 10
|
1月前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
37 1
|
2月前
|
机器学习/深度学习 人工智能 算法
揭开深度学习与传统机器学习的神秘面纱:从理论差异到实战代码详解两者间的选择与应用策略全面解析
【10月更文挑战第10天】本文探讨了深度学习与传统机器学习的区别,通过图像识别和语音处理等领域的应用案例,展示了深度学习在自动特征学习和处理大规模数据方面的优势。文中还提供了一个Python代码示例,使用TensorFlow构建多层感知器(MLP)并与Scikit-learn中的逻辑回归模型进行对比,进一步说明了两者的不同特点。
95 2
|
2月前
|
SQL 监控 关系型数据库
SQL错误代码1303解析与处理方法
在SQL编程和数据库管理中,遇到错误代码是常有的事,其中错误代码1303在不同数据库系统中可能代表不同的含义
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
76 0

推荐镜像

更多
下一篇
DataWorks