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