思路来源:b站up主:【香辣鸡排蛋包饭】。本博只是自我学习整理所用
主页: space.bilibili.com/363956974?s…
此篇是按照知识点进行分类的
1.链表
-
链表: 通过指针串联在一起的线性结构,每一个节点是又两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)
-
节点定义
var node = function(element) {
this.element = element; // 存放节点内容
this.next = null; // 指针
}
2.链表算法
1. 一般
1.JZ6 从尾到头打印链表
- 1.思路
- 2. 方法
-
- 代码
function printListFromTailToHead(head)
{
// write code here
var str=[]; // 定义预存数组
while(head){
str.unshift(head.val); // unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度
head = head.next;
}
return str;
}
module.exports = {
printListFromTailToHead : printListFromTailToHead
};
递归法
function printListFromTailToHead(head)
{
// write code here
//一直递归到链表尾部,从尾部往前将值放进数组,放进数组的方式是从尾部添加(push)
var arr=[];
getArr(head,arr);
return arr;
}
function getArr(head,arr){
if(head!==null){
getArr(head.next, arr);
arr.push(head.val);
}
}
module.exports = {
printListFromTailToHead : printListFromTailToHead
};
2.JZ52 两个链表的第一个公共节点
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围: n \le 1000n≤1000 要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
- 1.思路
- 2.方法 1.定义两个头结点 2.只要头结点不相等,就按照特定轨迹移动 结点不为空,就向后一个结点移动,否则就到另一个链表 3.跳出循环,找到了公共节点,pA pB都是空的,所以直接返回pA
- 3.代码
function FindFirstCommonNode(pHead1, pHead2)
{
// write code here
// a1 → a2 → a3↘
// c1 → c2 → c3
// b1 →b2 →b3↗
let pA = pHead1, pB = pHead2 // 定义两个头结点
while (pA !== pB) { // 只要两个头结点不相等, 就按照特定的轨迹移动
pA = pA ? pA.next : pHead2 // pA不为空,就移动端到pA的后续节点,否则移动到B链表的头结点
pB = pB ? pB.next : pHead1 //
}
return pA
}
module.exports = {
FindFirstCommonNode : FindFirstCommonNode
};
2.反转
1.JZ24 反转链表
输入一个链表,反转链表后,输出新链表的表头。
- 1.思路
链表的指针为单向,所以不能查找目前结点的前一个结点
从头开始,用pre保存上一项,cur当前项,tmp下一项。
pre和cur一组,不停移动,知道cur为null,则pre指向的是新的结点。
pre结点可以用来反转方向,为了避免反转之后链表断开,用temp结点暂时保存next结点
- 2.方法
1.初始化定义各结点,
pre = null,cur = head,temp = null
2.只要cur没有走到最后的null,存储next结点,断开(next= pre),pre,cur向末移动
3.返回pre - 3.代码
function ReverseList(pHead)
{
// write code here
let pre = null; //前结点
let cur = pHead; //当前结点
let temp = null; //初始化的下一个结点
while(cur !== null){
temp = cur.next;
//断开到下一个结点的指针,然后pre、cur进行同时移动
cur.next = pre; //pre和next是否同时为null,不是则断开next的(赋值为pre)
pre = cur; // pre移动
cur = temp; //temp移动
}
return pre
}
module.exports = {
ReverseList : ReverseList
};
2. NC50 链表中的节点每k个一组翻转
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。
-
- 思路
-
- 代码
function reverseKGroup( head , k ) {
let pre = null, cur = head, dummy = head
// 判断链表长度是否够k个翻转
for (let i = 0; i < k; i++) {
if(!dummy) return head
dummy = dummy.next
}
// 每k个进行翻转
for (let i=0; i<k; i++) {
let temp = cur.next // 暂存下一个内容
cur.next = pre // 修改指针
// 更新节点
pre = cur
cur = temp
}
// 进行下一次k个翻转
head.next = reverseKGroup(cur , k)
return pre
}
3.NC21 链表内指定区间反转
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n)O(n),空间复杂度 O(1)O(1)。
- 1.思路
- 2.方法
- 3.代码
function reverseBetween( head , m , n ) {
// write code here
let dummy = new ListNode(0)
dummy.next = head
let temp = dummy // 翻转区间起始前一个的位置
for (let i = 0; i< m-1; i++) {
temp = temp.next
}
let pre = null
let cur = temp.next // cur 指向已经翻转的链表的结尾
// let smHead = temp.next
for (let i = 0; i<= n-m; i++) { // <=
let next = cur.next // next进行链表的翻转
cur.next = pre
// 移动
pre = cur
cur = next
}
temp.next.next = cur
temp.next = pre
return dummy.next
}
4.NC96 判断一个链表是否为回文结构
-
1.思路 使用快慢指针,将后半段链表进行反转,例如:1->2->3->2->1,反转后成为1->2->3<-2<-1,进行比较,最后将链表还原。
-
2.方法
-
3.代码
function isPail( head ) {
// write code here
// 1.定义快慢指针法
let slow = head,
fast = head,
pre
if (!fast.next) return true // 1.只有一个,直接返回
// 2.遍历链表,移动快慢指针
while (fast && fast.next) {
pre = slow
slow = slow.next
fast = fast.next.next
}
pre.next = null // 左右断开
// 3.反转链表,此时slow到了中点
let head2
while(slow) {
const temp = slow.next
slow.next = head2
// 移动结点
head2 = slow
slow = temp
}
while(head && head2) {
if (head.val !== head2.val) return false
head = head.next
head2 = head2.next
}
return true
}
3.合并
1.JZ25 合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
- 1.思路
- 2.方法
1.初始化一个新的链表(内容为空)
2.判断两连边是否有空
3.比较两者的值,head→小的那个,小的指针向下移动。head向下移动。 4.判断谁先到头,则将另一个剩下的全部贴给head。 - 3.代码
function ListNode(x){
this.val = x;
this.next = null;
}
function Merge(pHead1, pHead2)
{
var head = new ListNode(null)//初始化一个节点值为0的空节点
let dummy = head; //存储新链表的当前结点
if(pHead1 == null) return pHead2;
if(pHead2 == null) return pHead1;
if(pHead1 == null && pHead2 == null) return null;
while(pHead1 && pHead2){
if(pHead1.val < pHead2.val){
head.next = pHead1
pHead1 = pHead1.next
}else{
head.next = pHead2
pHead2 = pHead2.next
}
head = head.next
}
if(!pHead1){
head.next = pHead2; //pHead1空了的话
}else if(!pHead2){
head.next = pHead1
}
return dummy.next
}
module.exports = {
Merge : Merge
};
2.C51 合并k个已排序列表
输入:[{1,2},{3,4,5},{6}]
输出:{1,2,3,4,5,6}
-
1.思路
-
2.方法 1.遍历所有节点,将结点值提取到数组中
2.对数组进行升序排序
3.遍历数组,将数组内容重新转换为链表
-
3.代码
function mergeKLists( lists ) {
// write code here
if (!lists || lists.length == 0) return null
let arr = []
// 1.遍历所有结点,将结点值提取到数组中
lists.forEach( list => {
let cur = list // 用一个cur来存储
while (cur) {
arr.push(cur.val)
cur = cur.next
}
})
let res = new ListNode(0) // 新链表的根节点
let cur = res // 记录上一个节点
// 2.对数组进行升序排序 sort((a,b) => a-b)
arr.sort((a, b) => a-b).forEach(val => { // 3.遍历数组,将数组内容转化为链表
let node = new ListNode(val) // 遍历,创建连接关系,将前一个节点耳朵next设置为当前结点
cur.next = node
cur = cur.next
})
return res.next
}
4.环
1.NC4 NC4 判断链表中是否有环
判断给定的链表中是否有环。如果有环则返回true,否则返回false。
-
1.思路 快慢指针法
-
function hasCycle( head ) {
if (!head || !head.next) return false // 必须先判断是否能使用快慢指针
let fast = head
let slow = head
while (fast.next && fast.next.next) {
fast = fast.next.next
slow = slow.next
if (slow == fast) return true
}
return false
}
2.JZ23 链表中环的入口节点
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
-
2.方法 1.采用快慢指针法,快指针走两步,慢指针走一步。两者相遇,及找到相遇节点
2.从相遇节点节点发出指针index1,从头结点发出index2,两指针步数相同,相遇的地方就是环形入口节点 -
3.代码
function EntryNodeOfLoop(pHead)
{
// write code here
let fast = pHead // 快指针
let slow = pHead // 慢指针
while (fast.next && fast.next.next) {// 判断快指针是否可走两步
fast = fast.next.next
slow = slow.next
if (slow == fast) { // 快慢指针相遇,此时从 head 到 相遇点,同时查找环形入口
let index1 = fast // 相遇节点发出index1
let index2 = pHead // 从头结点发出index2
while (index1 !== index2) {
index1 = index1.next
index2 = index2.next
}
return index2 // 找到了环形的入口
}
}
return null
}
5.删除
1.JZ76 删除链表中重复的节点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5
- 1.思路 添加一个头节点 头结点连接链表头部
设置pre 和cur指针。
pre指针指向当前确定不重复的节点,cur指针是当前工作的指针.
遇到重复的就跳过,一直向后进行
-
2.方法 1.定义新的头结点、头结点指针、当前结点指针
2.若有相同的,则一直找到最后一个相同的节点,找到后不保留删除,pre.next指向新的节点,更新cur
3.没有相同的,则两个指针一起移动 -
3.代码
function deleteDuplication(pHead)
{
// write code here
var p = { // 设置一个新的头结点
val: 0,
next: pHead
}
let pre = p // 头结点指针
let cur = p.next // 当前结点指针
while (cur) {
if (cur.next && cur.val === cur.next.val) {
// 找到最后一个相同结点
while (cur.next && cur.val === cur.next.val) {
cur = cur.next
}
pre.next = cur.next // 不保留删除,cur往下资源
cur = cur.next // 更新cur的下一个
} else {
pre = pre.next
cur = cur.next
}
}
return p.next
}
2.JZ18 删除链表中的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
1.此题对比原题有改动
2.题目保证链表中节点的值互不相同
3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
- 1.思路
-
2.方法 1.如果删除的是头结点的值,则直接返回后面的
2.如果删除的是中间的值,则指针移动
3.如果没找到值,则递归寻找 -
- 代码
function deleteNode( head , val ) {
// write code here
if (!head) return null
if (head.val === val) { // 1.如果删除的是头结点的值,则直接返回后面的
return head.next
}
if (head.next.val === val) { // 2.如果找到值所在的节点,则删除
head.next = head.next.next
} else { // 3.如果都不是,则向后移动,递归调用
deleteNode(head.next, val)
}
return head
}
3.NC53 删除链表的倒数第n个节点
给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
-
1.思路
-
2.方法 1.设置新的围头结点 2.快慢指针,快指针走n+1 3.慢指针和快指针同事移动,直到fast走到最后,slow到被删除节点之前
-
3.代码
var removeNthFromEnd = function(head, n) {
var pre = { // 设置一个新的伪头结点
val: 0,
next: head
}
var slow = pre;
var fast = pre;
// 1. 快指针走n+1步,这样slow才能指向删除节点的上一个节点
while(n>=0) {
fast = fast.next;
n--;
}
// 2.快慢指针同时移动
while(fast){
fast = fast.next;
slow = slow.next;
}
// 3.直到fast到最后,此时slow在所删除节点的上一位。删除节点,返回pre
slow.next = slow.next.next;
return pre.next;
};
6.JZ22链表中最后K个结点
输入一个链表,输出一个链表,该输出链表包含原链表中从倒数第k个结点至尾节点的全部节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
-
1.思路 Null处为fast指针,所找点为slow指针。
先让fast从起点处右移k步,然后slow和fast一起向右移动。
fast到达NULL时,slow正好在倒数第K个结点上。 -
2.方法 1.判断头结点为空或k<0;
2.初始化slow,fast指针
3.fast先走(循环到<k-1),若fast的下一个结点存在,就向下走,若不存在,则停下(return null) 4.若fast下一个结点存在,fast和slow一起向下走。 -
3.代码
function FindKthToTail( pHead , k ) {
// write code here
if (pHead == null || k<=0 ){////如果头节点为空或k的值小于0,就直接返回null
return null;
}
var fast = pHead;
var slow = pHead;
for(var i = 0; i< k-1; i++){//让第一个指针走k-1步
if(fast.next){//如果下一个节点存在就继续往下走,否则k小于链表长度,直接返回null
fast = fast.next;
}else{
return null
}
}
while(fast.next){
slow = slow.next;
fast = fast.next;
}
return slow;
}
module.exports = {
FindKthToTail : FindKthToTail
};
7.JZ35 复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。
- 1.思路
1.复制节点的值,并且放到原节点的后面
2.
-
2.方法
-
3.代码
function RandomListNode(x){
this.label = x;
this.next = null;
this.random = null;
}
function Clone(pHead)
{
if(pHead === null) return;
// write code here
// 1.复制节点的值,并且放到原节点的后面
var cur = pHead;
while(cur){
var curCopy = new RandomListNode(cur.label) //复制每一个节点
//并将节点插入到后面 【cur】【curCopy】,两个next指针先平移
curCopy.next = cur.next
cur.next = curCopy
cur = curCopy.next
}
// 2.构造random,从头结点开始
cur = pHead
while(cur){
if(cur.random) {
cur.next.random = cur.random.next
}
cur = cur.next.next //cur, curCopy,cur.(next)原本的
}
//3.拆分链表,从头结点开始
cur = pHead
var newHead =pHead.next // 记录返回的复杂链表的头结点
while(cur.next){
var temp = cur.next
cur.next = temp.next //间隔相连
cur = temp
}
return newHead
}
module.exports = {
Clone : Clone
};
8.NC40 两个链表生成相加链表
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
- 1.思路 利用栈来存储两个链表,然后用cnt存储进位,用tempNode进行节点插入
- 2.方法
- 3.代码
function addInList( head1 , head2 ) {
// write code here
let s1 = [], s2 = [] //用栈存链表的数,这样就能从末位计算
while (head1) {
s1.push(head1.val)
head1 = head1.next
}
while (head2) {
s2.push(head2.val)
head2 = head2.next
}
// 如果有进位,用cnt存储进位
let cnt = 0
let res = null // 结果指针
while (s1.length !== 0 || s2.length !== 0) {
let x1 = s1.length === 0 ? 0 :s1.pop()
let x2 = s2.length === 0 ? 0 :s2.pop()
let sum = x1 + x2 + cnt // 当前这位相加
cnt = Math.floor(sum / 10) // 更新当前加和是否有进位
// 进行节点插入!!!!!!!!!!
let tempNode = new ListNode(Math.floor(sum % 10))
tempNode.next = res // 暂时放到前面
res = tempNode // ????
}
if (cnt > 0) {
let tempNode = new ListNode(cnt)
tempNode.next = res
res = tempNode
}
return res
}
9.NC 单链表的排序
给定一个节点数为n的无序单链表,对其按升序排序。
-
1.思路 把链表拿出来放到一个数组里排好序,再放回链表里。
-
2.方法 1.把链表的值放到数组里 2.用sort进行升序排序 3.node回到head,对数组遍历,将数组的值放回链表
-
3.代码
function sortInList( head ) {
// write code here
if (!head || !head.next) return head
// 定义一个新数组
let arr = []
let node = head
while(node) {
arr.push(node.val)
node = node.next
}
arr.sort((a,b) => a-b) // 升序排序
node = head
for (let item of arr) { // 对数组进行遍历
node.val = item
node = node.next
}
return head
}