题目地址:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/
题目描述:
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
题目示例:
二叉搜索树转为双向链表
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
解题思路
思路1:分析题目可知,首先,循环链表顺序是单调递增,所以,观察二叉搜索树的三种遍历方式,发现二叉搜索树的中序遍历(左子树->根节点->右子树)满足条件,即从小到大遍历树的节点,然后,考虑循环链表的指向问题,这里我们使用双指针pre和head,其中pre表示前驱节点,head表示头节点,cur表示当前节点,tail表示尾节点,则双向链表的指向问题可以表示为pre->right=cur;cur->left=pre;这样我们就构造好了双向链表,最后就是循环链表的问题了,将尾节点的左指针指向头节点,头节点的右指针指向尾节点,即head->left=tail;tail->right=head。
思路2:类似于思路1,不过这里使用数组arr存储中序遍历的每个节点,这样arr数组中元素顺序单调递增,然后利用一层循环使得相邻元素连接构成双向链表,最后改变首尾元素的指向,即可得到循环双向链表。
程序源码
中序遍历+递归
/* // Definition for a Node. class Node { public: int val; Node* left; Node* right; Node() {} Node(int _val) { val = _val; left = NULL; right = NULL; } Node(int _val, Node* _left, Node* _right) { val = _val; left = _left; right = _right; } }; */ class Solution { public: Node* pre = nullptr, *head = nullptr, *tail = nullptr; Node* treeToDoublyList(Node* root) { if(root == nullptr) return nullptr; inorder(root); //连接首尾节点 head->left = tail; tail->right = head; return head; } void inorder(Node* cur) { if(cur == nullptr) return; //递归出口 inorder(cur->left); //遍历左子树 if(pre != nullptr) pre->right = cur; else head = cur; cur->left = pre; pre = cur; tail = cur; inorder(cur->right); //遍历右子树 } };
数组
/* // Definition for a Node. class Node { public: int val; Node* left; Node* right; Node() {} Node(int _val) { val = _val; left = NULL; right = NULL; } Node(int _val, Node* _left, Node* _right) { val = _val; left = _left; right = _right; } }; */ class Solution { public: vector<Node*> arr; Node* treeToDoublyList(Node* root) { if(root == nullptr) return nullptr; inorder(root); for(int i = 0; i < arr.size() - 1; i++) { arr[i]->right = arr[i + 1]; arr[i + 1]->left = arr[i]; } arr[arr.size() - 1]->right = arr[0]; arr[0]->left = arr[arr.size() - 1]; return arr[0]; } void inorder(Node* cur) { if(cur == nullptr) return; //递归出口 inorder(cur->left); //遍历左子树 arr.push_back(cur); //装入当前节点 inorder(cur->right); //遍历右子树 } };
参考文章