这么多天以来学到最多的一道题。
这题真的是把链表和树的知识综合起来了,需要融会贯通。
有两种做法,很遗憾自己写的时候一种都没想出来,只想到将链表先转换为数组这种简单方法……
第一个知识点是快慢指针,这种做法在找出长链表的二分点、三分点很常用,只需要遍历一遍就能得到链表的二分位置。
具体做法是设置快慢指针,快指针一次走两步,慢指针一次走一步,在快指针到达链表末尾时慢指针正好到达链表的二分位置。
通过这种方法,我们每次遍历找出链表中点,将中点作为前链表终点和后链表起点,再将前链表和后链表遍历分别作为中点树的左右子树,如此反复递归,即可得到最终树。
/*** 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后移。