<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>两两交换节点位置</title>
</head>
<body>
<script>
class ANode {
constructor(value) {
this.value = value;
this.next = null;
}
}
// 从一个数组,创建一个nodeList并返回它的head
function createNodeList(nodeList) {
if(!nodeList || nodeList.length===0) return null;
const head = new ANode(nodeList[0]);
let cur = head;
for(let i=0; i<nodeList.length-1; i++) {
cur.next = new ANode(nodeList[i+1]);
cur = cur.next;
}
return head;
}
// 打印nodeList, eg: nodeList: (1) => (2) => (3) => (4) => null
function printNodeList(head, nodeListName = "nodeList") {
let str = `(${head.value}) => `;
while(head && head.next!==null) {
head = head.next;
str += `(${head.value}) => `;
}
str += "null";
console.log(`${nodeListName}: ${str}`);
}
/**
* @address: https://juejin.cn/post/7218584877910212667
* @description: 递归回溯方法 TC:O(n) SC:O(n)
* @author: JunLiangWang
* @param {*} node
* @return {*}
*/
function recursionBacktracking(node) {
/**
* 该方案利用递归回溯的方式,递归判断当前节点以及其后一个节点是否存在,
* 如果不存在,证明已无节点可交换,则直接返回当前节点;否则,则将当前
* 节点与其下一个节点进行交换。交换过程为:先将当前节点的下一个节点取
* 出并保存(nextNode),然后将当前节点的下一个节点更新为nextNode后续
* 节点继续递归的结果,然后将nextNode的下一个节点更新为当前节点,此时
* nextNode则变更为当前节点的前一个节点,返回nextNode即可。
*/
// 判断当前节点与其下一个节点是否存在,如果存在则将两节点位置交换
if (node && node.next) {
// 先保存当前节点的下一个节点(nextNode)
let nextNode = node.next;
// 将当前节点的下一个节点更新为nextNode后续节点继续递归的结果
node.next = recursionBacktracking(nextNode.next);
// 将nextNode的下一个节点更新为当前节点
nextNode.next = node;
// 此时nextNode已为当前节点的前一个节点,返回nextNode即可
return nextNode;
}
// 不存在证明已无可交换节点,直接返回即可当前节点即可
else {
return node;
}
}
/**
* @address: https://juejin.cn/post/7218584877910212667
* @description: 迭代法 TC:O(n) SC:O(n)
* @author: JunLiangWang
* @param {*} head
* @return {*}
*/
function iteration(head) {
/**
* 该方案利用迭代,每次遍历链表的两个节点,将其交换,直至遍历结束
*/
// 如果当前节点为空或其后一个节点为空,证明无需要交换的节点,直接返回当前节点即可
if (!head || !head.next) return head;
// 新的头节点为原链表的第二个节点
const NEW_HEAD = head.next;
// 上一个节点初值为空
let lastNode = null;
// 当前节点初值为原链表第一个节点
let currentNode = head;
// 当前节点不为空且其下一个节点也不为空,证明仍存在需要交换的节点
while (currentNode && currentNode.next) {
// 将当前节点的下一个节点取出保存(nextNode)
let nextNode = currentNode.next;
// 将当前节点的下一个节点更新为nextNode的下一个节点
currentNode.next = nextNode.next;
// nextNode的下一个节点更新为当前节点,此时位置已完成交换
nextNode.next = currentNode;
// 如果存在上一个节点,则需要将上一个节点的下一个节点更新为
// 已经交换位置的nextNode
if (lastNode) lastNode.next = nextNode;
// 上一个节点更新为当前节点
lastNode = currentNode;
// 当前节点更新为其下一个节点
currentNode = currentNode.next;
}
// 返回新链表
return NEW_HEAD;
}
// 单向链表转换成数组
function nodeListToArray(head) {
if(!head) return [];
const res = [head];
while(head && head.next!==null) {
head = head.next;
res.push(head);
}
return res;
}
// 数组转换法
function byArray(head) {
const transformedArr = nodeListToArray(head);
if(!transformedArr || transformedArr.length===0) return null;
for(let i=0; i<transformedArr.length; i++) {
let pre = transformedArr[i];
let next = transformedArr[i+1];
// 元素个数为奇数时,最后一个不交换
if(next) {
[pre, next] = [next, pre];
}
}
return createNodeList(transformedArr[0]);
}
const nodeList1 = createNodeList([1, 2, 3, 4]);
const nodeList2 = createNodeList([1, 2, 3, 4]);
const nodeList3 = createNodeList([1, 2, 3, 4]);
printNodeList(nodeList1, "nodeList1");
printNodeList(nodeList2, "nodeList2");
printNodeList(nodeList3, "nodeList2");
// 递归法
const newHead1 = recursionBacktracking(nodeList1);
printNodeList(newHead1, "递归法");
// 迭代法
const newHead2 = iteration(nodeList2);
printNodeList(newHead2, "迭代法");
// 数组转换法
const newHead3 = byArray(nodeList3);
printNodeList(newHead2, "数组转换法");
</script>
</body>
</html>