算法之 有序链表和平衡二叉树

题目描述:

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.

给定一个有序的链表,要求构建一颗平衡二叉查找树。

解析:二叉查找树的中序遍历的结构就是一颗二叉查找树,要使得最终的二叉查找树的结构尽可能的平衡,也就是说只需要将左右两边的节点数尽可能的均匀,(注意,此处的均匀是一个递归的概念,也就是说每一个节点的左右子树的节点个数都应该尽量均匀)。我们首先想到了二分。

于是下面的代码应运而生:

    TreeNode *sortedListToBST(ListNode *head)
    {
        if(head == nullptr) return (TreeNode *)nullptr;
        ListNode *fast=head->next;
        ListNode *slow=head , *prev= nullptr;
        while(fast && fast->next)
        {
            prev=slow;
            fast=fast->next->next;
            slow=slow->next;
        }
        //have left tree or not
        if(prev)prev->next=nullptr; //此处直接将next赋值为空,那么链表的结构便被改变了。
        else head=nullptr;
        TreeNode *node = new TreeNode(slow->val);
        fast=slow->next;
        slow->next=nullptr;
        TreeNode *L = sortedListToBST(head);
        TreeNode *R = sortedListToBST(fast);
        node->left=L;
        node->right=R;
        return node;
    }

上述代码虽然可以完成我们需要的平衡二叉查找树,但是上述代码却有一个致命的缺点:那就是内存泄露,

上述代码在执行过程中将原始的链表的节点弄得面目全非,那么也就是说如果那么节点都是通过new操作符得来的话,那么在执行完这个函数之后,有大部分节点的指针已经无法访问到,导致内存泄露。

于是,我们不能使用拆分链表的形式来构建平衡二叉查找树,我们不能改变链表的结构,那么怎么办呢。我们需要做的就是记录当前链表的头结点和长度即可。实现如下:

class Solution {
    //之前的代码虽然AC了,但是会改变list的结构,这样必导致内存泄露,于是前面的代码全部都是错误的。
private:
    int Len(ListNode *head)
    {
        int ret=0;
        while(head)
        {
            ++ret;
            head=head->next;
        }
        return ret;
    }
    TreeNode *helper(ListNode *head, int len)
    {
        if(head == nullptr || len <= 0 ) return nullptr;
        ListNode *cur=head;
        int mid=(len+1)/2-1;
        int tmp=mid;
        while(tmp)
        {
            cur=cur->next;
            --tmp;
        }
        TreeNode *root = new TreeNode(cur->val);
        root->left = helper(head,mid);
        root->right = helper(cur->next,len-mid-1);
        return root;
    }
public:
    TreeNode *sortedListToBST(ListNode *head) {
        const int n = Len(head);
        TreeNode *root=helper(head,n);
        return root;
    }
};

于是上述代码中的内存泄露的问题便消除了,因为在整个过程中我们都没有改变链表的任何节点的指向。

这里,最最值得一提的便是链表的销毁。在销毁链表时,要么使用返回值,要么传入的参数是链表的引用,或者是指针的指针,否则也会有内存的隐患:

也就是说删除链表的代码为:

void destory(ListNode * &head)
{
    ListNode *tmp=nullptr;
    while(head)
    {
        tmp=head->next;
        delete head;
        head = nullptr ; //删除指针的额安全的方式,便是delete之后将该指针赋值为空
        head=tmp;
    }
    head = nullptr; //当然,头结点也不例外,如果传入的参数不是指针的指针或者是引用的话,那么head虽然delete掉了,但是该指针在下一次被赋值之前依然指向内存的某一块区域。
}
时间: 2024-08-25 12:38:10

算法之 有序链表和平衡二叉树的相关文章

数据结构:根据有序链表构造平衡二叉树

#include <iostream> #include <stack> using namespace std; struct Node { int data; Node* next; Node(int d = int()):data(d){} }; class List { friend class T; public: List():first(NULL){} void Insert(int a[],int n) { Node *p = NULL; for(int i=0;i

[LeetCode] 109. 有序链表转换二叉搜索树 ☆☆☆(递归)

描述 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: [-10, -3, 0, 5, 9], 一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树: 0 / \ -3 9 / / -10 5 解析 有序链表,平衡二叉树,很自然想到将链表一分为二,两部分就是中间节点的左右节点.递归执行左右2个部分的节点

每天一个小算法(2)----合并两个有序链表

每天一个小算法还是有点没时间,尽量抽出时间写一写. 今天是合并有序的链表,对单链表有点忘了,尤其是指针指来指去的,有点晕,幸好基础还算好,想了想还是能想回来. 代码使用随机数函数生成一个链表,然后对链表排序,最后合并链表并打印,删除链表的函数于算法无关紧要,所以未实现^_^. 在Linux/g++下编译运行成功. 合并思路:和合并数组有些类同,比较两个节点的元素大小然后将小的摘下来尾插到链表bList中,然后指针指向下一个节点,最后直接把非空的链表合并到bList的末尾. 1 #include

算法总结之 打印两个有序链表的公共部分

给定两个有序链表的头指针 head1 和 head2,打印两个链表的公共部分 思路: 有序嘛, 如果head1 的值小于 head2, head1往下移动 如果head2的值小于head1,head2往下移动 如果相等,打印这个值,然后同时向下移动 两个有一个为null, 整个过程停止 package TT; import java.time.temporal.ValueRange; public class Test84 { public class Node{ public int valu

经典算法——合并K个有序链表

一.题目要求: 将K个有序链表合并为一个有序链表 二.实现方法: 方法一:利用最小堆方法 用一个大小为K的最小堆(用优先队列+自定义降序实现)(优先队列就是大顶堆,队头元素最大,自定义为降序后,就变成小顶堆,队头元素最小),先把K个链表的头结点放入堆中,每次取堆顶元素,然后将堆顶元素所在链表的下一个结点加入堆中. 整体测试代码: #include <vector> #include <iostream> #include<queue> #include<set&g

23. Merge k Sorted Lists 合并K个有序链表

这道题是21题合并2个有序链表的升级版本,看了许多解题思路: A:直接暴力解锁,全部放进一个堆,然后依次吐出来: B:利用21题的算法,循环一次做两两合并,这样就得到结果:但是时间复杂度有点差: C:利用归并排序思想,进行分治:其实就是利用递归,牺牲空间,提升时间效率: 存在的问题是:看过了许多解答后发现,大家基于的给定数据类型是 List<ListNode>/ArrayList<ListNode>,然后,现在系统更新了,给的数据类型是 ListNode[] lists,所以,我现

有序链表的用途

有序链表主要用途就是用于排序,大家都知道,数组的插入排序算法,排序的时候需要大量的复制数据,这样效率就会很低,用有序链表实现无序数组的排序这样可以减少 很多次的数据的复制.效率大大提高. package 有序链表; public class Link { public double dd; public Link next; public Link(double dd){ this.dd = dd; } //打印节点的方法 public void displayLink(){ System.ou

LeetCode(109):有序链表转换二叉搜索树

Medium! 题目描述: 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: [-10, -3, 0, 5, 9], 一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树: 0 / -3 9 / / -10 5 解题思路: 这道题是要求把有序链表转为二叉搜索树,和之前那道Convert Sorted A

有序链表转换二叉搜索树

问题描述: 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: [-10, -3, 0, 5, 9], 一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树: 0 / -3 9 / / -10 5 解题思路: 将链表先转为数组,之后方法与将有序数组转换为二叉搜索树相同. 实现代码: private sta