zoukankan      html  css  js  c++  java
  • 有序链表转换二叉搜索树--链表

    题目

    给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

    本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

    示例:

    给定的有序链表: [-10, -3, 0, 5, 9],
    
    一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
    
          0
         / 
       -3   9
       /   /
     -10  5

    思想

    简单回复一下,什么是平衡二叉树?

    平衡二叉树(Balanced Binary Tree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

    中心

    题目中最重要的要求是需要利用链表中的节点,构建一颗高度平衡的二叉搜索树,好消息是链表中的元素是升序的。

    众所周知,一棵二叉搜索树是一棵有根二叉树并且对于所有节点满足特殊的性质:对于树中任意一个点,它的权值必然≥ 所有左子树节点的权值,≤ 所有右子树节点的权值。因为二叉树具有递归的子结构,二叉搜索树也同理:所有子树也是二叉搜索树。

    当前方法和下一个方法的主要思路是:

    给定列表中的中间元素将会作为二叉搜索树的根,该点左侧的所有元素递归的去构造左子树,同理右侧的元素构造右子树。这必然能够保证最后构造出的二叉搜索树是平衡的。

    算法

    1. 由于我们得到的是一个有序链表而不是数组,我们不能直接使用下标来访问元素。我们需要知道链表中的中间元素。
    2. 我们可以利用两个指针来访问链表中的中间元素。假设我们有两个指针 slow_ptr 和 fast_ptr。slow_ptr 每次向后移动一个节点而 fast_ptr 每次移动两个节点。当 fast_ptr 到链表的末尾时 slow_ptr 就访问到链表的中间元素。对于一个偶数长度的数组,中间两个元素都可用来作二叉搜索树的根。
    3. 当找到链表中的中间元素后,我们将链表从中间元素的左侧断开,做法是使用一个 prev_ptr 的指针记录 slow_ptr 之前的元素,也就是满足 prev_ptr.next = slow_ptr。断开左侧部分就是让 prev_ptr.next = None。
    4. 我们只需要将链表的头指针传递给转换函数,进行高度平衡二叉搜索树的转换。所以递归调用的时候,左半部分我们传递原始的头指针;右半部分传递 slow_ptr.next 作为头指针。

    代码

     /**
    * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    
    
    private ListNode findMiddleElement(ListNode head) {
    
        // The pointer used to disconnect the left half from the mid node.
        ListNode prevPtr = null;
        ListNode slowPtr = head;
        ListNode fastPtr = head;
    
        // Iterate until fastPr doesn't reach the end of the linked list.
        while (fastPtr != null && fastPtr.next != null) {
          prevPtr = slowPtr;
          slowPtr = slowPtr.next;
          fastPtr = fastPtr.next.next;
        }
    
        // Handling the case when slowPtr was equal to head.
        if (prevPtr != null) {
          prevPtr.next = null;
        }
    
        return slowPtr;
      }
    
      public TreeNode sortedListToBST(ListNode head) {
    
        // If the head doesn't exist, then the linked list is empty
        if (head == null) {
          return null;
        }
    
        // Find the middle element for the list.
        ListNode mid = this.findMiddleElement(head);
    
        // The mid becomes the root of the BST.
        TreeNode node = new TreeNode(mid.val);
    
        // Base case when there is just one element in the linked list
        if (head == mid) {
          return node;
        }
    
        // Recursively form balanced BSTs using the left and right halves of the original list.
        node.left = this.sortedListToBST(head);
        node.right = this.sortedListToBST(mid.next);
        return node;
      }
  • 相关阅读:
    笔记(二) C#sql语句
    [叩响C#之门]写给初学者:多线程系列(七)——互锁(Interlocked类)
    C# Async与Await的使用
    C#线程锁使用全功略
    一个C#的加锁解锁示例
    【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)
    Control.BeginInvoke()和delegate的BeginInvoke()的区别
    crm04 action操作 和 多级过滤
    VIM和sed 替换字符串方法
    解决Centos关闭You have new mail in /var/spool/mail/root提示(转)
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/12012558.html
Copyright © 2011-2022 走看看