单双链表基类
export default class SNode{
public value:number;
public nextN:SNode;
constructor(val:number,nextN:SNode){
this.value = val;
this.nextN = nextN;
}
}
export default class DNode{
public value:number;
public nextN:DNode;
public lastN:DNode;
constructor(val:number,nextN:DNode,lastN:DNode){
this.value = val;
this.nextN = nextN;
this.lastN = lastN;
}
}
创建单双链表
/**
* 创建单链表(从后插入)
*/
createSNodeList(arr:number[]):SNode{
let head;
let p;
head = new SNode(arr[arr.length - 1],null);
if(arr.length == 1) return head;
for(let i = arr.length - 2;i>=0;i--){
p = new SNode(arr[i],head);
head = p;
}
return head;
}
/**
* 创建单链表(从前插入)
*/
createSNodeList1(arr:number[]):SNode{
let head;
let p;
head = new SNode(arr[0],null);
p = head;
if(arr.length == 1) return head;
for(let i = 1;i<arr.length;i++){
p.nextN = new SNode(arr[i],null);
p = p.nextN;
}
return head;
}
/**
*创建双链表(从后插入)
*/
createDNodeList(arr: number[]): DNode {
let head;
let p;
head = new DNode(arr[arr.length - 1], null, null);
if (arr.length == 1) return head;
for (let i = arr.length - 2; i >= 0; i--) {
p = new DNode(arr[i], head, null);
head.lastN = p;
head = p;
}
return head;
}
/**
* 创建双链表(从前插入)
*/
createDNodeList1(arr: number[]): DNode {
let head;
let p;
head = new DNode(arr[0], null, null);
p = head;
if (arr.length == 1) return head;
for (let i = 1; i < arr.length; i++) {
p.nextN = new DNode(arr[i], null, p);
p = p.nextN;
}
return head;
}
反转单双链表
/**
* 反转单链表
*/
reverseSNodeList(head: SNode) {
let p = head;
let p1 = p.nextN;
p.nextN = null;
let p2;
while (p1 != null) {
p2 = p1.nextN
p1.nextN = p;
p = p1;
p1 = p2;
}
head = p;
return head;
}
/**
* 反转双链表
*/
reverseDNodeList(head:DNode){
let p = head;
let p1;
do{
p1 = p.nextN;
p.nextN = p.lastN;
p.lastN = p1;
if(p1 != null) p = p1;
}while(p1 == null);
head = p;
return head;
}
打印单双链表
/**
* 正序打印单双链表
*/
printANodeList(head: SNode | DNode) {
let p = head;
let str = "";
while (p != null) {
str += p.value;
if (head instanceof SNode) str += "->"
else str += "<->";
p = p.nextN;
}
console.log("printANodeList", str);
}
/**
* 逆序打印双链表(测试用)
*/
printDNodeList(head: DNode) {
let p = head;
let str = "";
while (p.nextN != null) {
p = p.nextN;
}
while (p != null) {
str += p.value;
str += "<->"
p = p.lastN;
}
console.log("printDNodeList", str);
}
打印两个有序单链表(无环)中相同的部分
/**
* 打印两个有序单链表(无环)中相同的部分
*/
elementsInTwoSList(head1: SNode, head2: SNode) {
let p1 = head1;
let p2 = head2;
let str = "";
while (p1 != null && p2 != null) {
if (p1.value < p2.value) {
p1 = p1.nextN;
} else if (p1.value == p2.value) {
str += p1.value;
str += ","
p1 = p1.nextN;
p2 = p2.nextN;
} else {
p2 = p2.nextN;
}
}
console.log("elementsInTwoSList", str);
}
练习:判断一个单链表是否为回文结构 比如 1->2->3->2->1 或 1->2->3->3->2->1
/**
* 要求:时间复杂度为O(N),额外空间复杂度为O(N)
* 技巧:快慢指针,慢指针来到中间位置后,利用数组存储后1/2链表节点,依次出栈进行判断
*/
isPalindrome(head: SNode) {
let p = head;
let length = 0;
let s = head;
let f = head;
let list = [];
let stepsN = 1;
while (p != null) {
length++;
p = p.nextN;
}
if (length <= 1) return true;
let last = length % 2 == 0 ? length - 1 : length;
while (stepsN != last) {
f = f.nextN.nextN;
s = s.nextN;
stepsN += 2;
}
s = s.nextN;
while (s != null) {
list.push(s);
s = s.nextN;
}
p = head;
while (list.length > 0) {
if (list.pop().value != p.value) return false;
p = p.nextN;
}
return true;
}
/**
* 要求:时间复杂度为O(N),额外空间复杂度为O(1)
* 技巧:快慢指针,慢指针来到中间位置后,调转后1/2链表指针方向,而后依次判断。
*/
isPalindrome1(head: SNode) {
let p = head;
let length = 0;
let s = head;
let f = head;
let stepsN = 1;
let isPalin = true;
while (p != null) {
length++;
p = p.nextN;
}
if (length <= 1) return true;
let last = length % 2 == 0 ? length - 1 : length;
while (stepsN != last) {
f = f.nextN.nextN;
s = s.nextN;
stepsN += 2;
}
let lastN;
if (length % 2 == 0) s = s.nextN;
lastN = this.reverseSNodeList(s);
p = head;
let p1 = lastN;
while (p1 != null) {
if (p1.value != p.value) {
isPalin = false;
break;
}
p1 = p1.nextN;
p = p.nextN;
}
let newN;
newN = this.reverseSNodeList(lastN) //恢复后半截的指针方向
s.nextN = newN;
return isPalin;
}
练习:在单链表解决荷兰国旗问题,等于的数在中间,小于的数在左边,大于的数在右边
/**
* 要求:时间复杂度为O(N),空间复杂度为O(1)
*/
holandInNodeList(head: SNode, num: number) {
let lH; //大于区域头指针
let lL; //大于区域尾指针
let eH; //等于区域头指针
let eL; //等于区域尾指针
let sH; //小于区域头指针
let sL; //小于区域尾指针
let p = head;
while (p != null) {
if (p.value == num) {
if (!eH) {
eH = p;
eL = p;
} else {
eL.nextN = p;
eL = p;
}
} else if (p.value < num) {
if (!sH) {
sH = p;
sL = p;
} else {
sL.nextN = p;
sL = p;
}
} else {
if (!lH) {
lH = p;
lL = p;
} else {
lL.nextN = p;
lL = p;
}
}
p = p.nextN;
}
lL.nextN = null;
if (sH) {
if (eH) {
sL.nextN = eH;
if (lH) eL.nextN = lH;
} else if (lH) sL.nextN = lH;
else sL.nextN = null;
return sH;
} else if (eH) {
if (lH) eH.nextN = lH;
else eL.nextN = null;
return eH;
} else {
return lH;
}
}
含有随机指针的链表,即randN指针是单链表结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。
export default class DNode{
public value:number;
public nextN:DNode;
public lastN:DNode;
constructor(val:number,nextN:DNode,lastN:DNode){
this.value = val;
this.nextN = nextN;
this.lastN = lastN;
}
}
创建随机指针链表
/**
* 用节点数组的方式来随机创建一个含有随机指针节点的链表
*/
createARNodeListRandomly() {
let list: RNode[] = [];
let length = Math.floor(Math.random() * 10) + 1;
let head = new RNode(Math.floor(Math.random() * 10), null, null);
let p = head;
while (length > 0) {
let rNode = new RNode(Math.floor(Math.random() * 10), null, null);
list.push(rNode);
p.nextN = rNode;
p = rNode;
length--
}
for (let node of list) {
if (Math.random() > 0.5) node.randN = null;
else node.randN = list[Math.floor(Math.random() * list.length)]
}
return head;
}
练习:复制一个含有随机指针节点的链表。
/**
* 方法一:用哈希结构Map来实现
*/
copyARNodeList(head: RNode) {
let map = new Map<RNode, RNode>();
let p = head;
let head1;
while (p != null) {
map.set(p, new RNode(head.value, null, null));
p = p.nextN;
}
for (let node of map.keys()) {
if (!head1) head1 = map.get(node);
map.get(node).nextN = map.get(node.nextN);
map.get(node).randN = map.get(node.randN);
}
return head1;
}
随机产生带环的单链表
/**创建带环随机的单链表*/
createSNWithCircle(arr: number[]) {
let head;
let p;
let circle;
if (arr.length <= 2) return null; //有环单链表至少有三个节点
let ran = Math.floor(Math.random() * (arr.length - 2)) + 1;//至少有一个节点在环外
head = new DNode(arr[0], null, null);
p = head;
if (arr.length == 1) return head;
for (let i = 1; i < arr.length; i++) {
p.nextN = new DNode(arr[i], null, p);
p = p.nextN;
if (i == ran) {
circle = p;
}
}
p.nextN = circle;
console.log("入环节点", circle.value);
return head;
}
讨论一个单链表是否有环,如果无环返回null,有环则并返回第一个入环节点(环转了一圈后又回到的第一个节点)
/**
* 方法一:利用数组,需要占用O(N)的内存空间
*/
nodeListHasCircle(head: SNode) {
if (head == null || head.nextN == null || head.nextN.nextN == null) return null;
let list = [];
let p = head;
while (p != null) {
if (list.indexOf(p) == -1) list.push(p);
else return p;
p = p.nextN;
}
return null;
}
/**
* 方法二:可用数学证明的快慢指针方法,空间复杂度O(1)
*/
nodeListHasCircle1(head: SNode) {
if (head == null || head.nextN == null || head.nextN.nextN == null) return null;
let s = head.nextN;
let f = s.nextN;
while (s != f) {
s = s.nextN;
if (s == null) return null;
f = f.nextN.nextN;
if (f == null) return null;
}
f = head;
while (s != f) {
s = s.nextN;
f = f.nextN;
}
return s;
}
练习:求两个单链表(可能有环可能无环)是否有共同的部分并返回第一个相交的节点
讨论:可能相交的情况为二者皆有环 或 二者皆无环。其他情况皆不可能相交。
nodeListIsInterset(head1: SNode, head2: SNode) {
let circle1 = this.nodeListHasCircle1(head1);
let circle2 = this.nodeListHasCircle1(head2);
if ((!circle1 && !circle2) || (circle1 && circle2 && circle1 == circle2)) {//判定两个单链表都没有环或 二者都有环且入环节点一致
let len1 = 0;
let len2 = 0;
let p1 = head1;
let p2 = head2;
while (p1 == circle1 && p2 == circle2) {
if (p1 != circle1) {
len1++;
p1 = p1.nextN;
} if (p2 != circle2) {
len2++;
p2 = p2.nextN;
}
}
let off = Math.abs(len1 - len2);
p1 = head1;
p2 = head2;
while (off == 0) {
if (Math.sign(len1 - len2) == 1) {
p1 = p1.nextN;
off--;
} else {
p2 = p2.nextN;
off--;
}
}
while ((circle1 && p1 == circle1.nextN) || (!circle1 && p1 == circle1)) {
if (p1 == p2) return p1;
p1 = p1.nextN;
p2 = p2.nextN;
}
return null;
} else if (circle1 && circle2 && circle1 != circle2) { //二者都有环但入环节点不一致
let p1 = circle1.nextN;
let p2 = circle2.nextN;
while (p1 == circle1) {
if (p1 == p2) return p1;
p1 = p1.nextN;
p2 = p2.nextN;
}
return null;
}
return null;
}