leetcode日记(96)有序链表转换二叉搜索树

这么多天以来学到最多的一道题。

这题真的是把链表和树的知识综合起来了,需要融会贯通。

有两种做法,很遗憾自己写的时候一种都没想出来,只想到将链表先转换为数组这种简单方法……

第一个知识点是快慢指针,这种做法在找出长链表的二分点、三分点很常用,只需要遍历一遍就能得到链表的二分位置。

具体做法是设置快慢指针,快指针一次走两步,慢指针一次走一步,在快指针到达链表末尾时慢指针正好到达链表的二分位置。

通过这种方法,我们每次遍历找出链表中点,将中点作为前链表终点和后链表起点,再将前链表和后链表遍历分别作为中点树的左右子树,如此反复递归,即可得到最终树。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    ListNode* head;
    TreeNode* structual(ListNode* left,ListNode* right){
        if(left==right||left==NULL) return nullptr;
        ListNode* quick=left;
        ListNode* slow=left;
        while(quick!=right&&quick->next!=right){
            quick=quick->next;
            quick=quick->next;
            slow=slow->next;
        }
        TreeNode* tree=new TreeNode(slow->val);
        tree->left=structual(left,slow);
        tree->right=structual(slow->next,right);
        return tree;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        this->head=head;
        return structual(head,nullptr);
    }
};

需要注意的一点是循环快慢指针时while的判断条件应有两个,因为链表长度有奇偶两种情况。

还有这里的left,right区间是左闭右开,意思是左被包含在左链表,右不包含在右链表(因为一开始的right是nullptr,而且在tree->left递归时right也就是slow不必后退,单向链表也不方便后退)。

第二种方法我觉得特别巧妙,没有利用快慢指针,而是利用中序遍历。

因为链表的顺序就是最后生成树的中序遍历,所以除了最初遍历链表得到链表长度,之后就不必遍历链表,而是每次取链表长度的中间数切分,这时不需要立刻添加中间节点的val,而是预留着,先遍历中间数前面的数,在等左子树全部有了数之后再去添加中间数的val。

这种做法不需要遍历多次链表,而是用序号代替链表位置,只要传入链表头节点,每次递归使节点变为next,然后就可以直接按照链表顺序添入树。

可以说是非常巧妙了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* dg(ListNode*& list,int begin,int end){
        if(begin>end||list==NULL) return nullptr;
        int middle=(begin+end)/2;
        TreeNode* tree=new TreeNode();
        tree->left=dg(list,begin,middle-1);
        tree->val=list->val;
        list=list->next;
        tree->right=dg(list,middle+1,end);
        return tree;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        ListNode* h=head;
        int length=0;
        while(h!=NULL){
            h=h->next;
            length++;
        }
        return dg(head,0,length-1);
    }
};

需要注意的是这里的left和right是左闭右闭,也就是right也要包含进树里,因为传入的左右节点其实是序号,只需要序号-1就可以传入左子树了,所以要顺便注意开始传入的是length-1。

另外还有一个特别要注意的地方,递归函数传入的ListNode*应该要加&,因为节点是要不断变化的,需要传入指针,才能在为中间节点赋值前让list后移。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值