给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
**示例 **
1 2 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ListNode { public var val: Int public var next: ListNode ? public init () { self .val = 0 ; self .next = nil ; } public init (_ val : Int ) { self .val = val; self .next = nil ; } public init (_ val : Int , _ next : ListNode ?) { self .val = val; self .next = next; } } func removeElements (_ head : ListNode ?, _ val : Int ) -> ListNode ? { guard head != nil else { return head } let dummyHead = ListNode () var newTail = dummyHead var curHead = head while curHead != nil { if curHead? .val != val { newTail.next = curHead newTail = curHead! } curHead = curHead? .next } newTail.next = nil return dummyHead.next }
解题思路: 这里用一个虚拟头结点来作为新链表的头结点会让代码更加的简洁,遍历传进来的链表,判断当前的链表是否是需要删除的如果不是需要删除的,让新链表尾结点下一个指向它,也就是尾部结点也是当前结点,最后把尾部结点置空,返回新节点头的下一个结点。
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头
示例 :
1 2 3 输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
提示:
每个链表中的节点数在范围 [1, 100]
内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 func addTwoNumbers (_ l1 : ListNode ?, _ l2 : ListNode ?) -> ListNode ? { guard l1 != nil else { return l2 } guard l2 != nil else { return l1 } let dummyHead = ListNode () var lastNode = dummyHead var p1 = l1 var p2 = l2 var carry = 0 while p1 != nil || p2 != nil { var v1 = 0 if p1 != nil { v1 = p1? .val ?? 0 p1 = p1? .next } var v2 = 0 if l2 != nil { v2 = p2? .val ?? 0 p2 = p2? .next } let sum = v1 + v2 + carry carry = sum / 10 lastNode.next = ListNode (sum % 10 ) lastNode = lastNode.next! } if carry > 0 { lastNode.next = ListNode (carry) } return dummyHead.next }
解题思路:
这里还是使用虚拟节点,每次去取两个链表的值相加的时候都需要再把进位值算进去,由于每次只能把sum的个位数作为新节点的值,所以进位的值只能留给后面的sum相加,获取新的值都插入到lastNode尾部,最后还需要处理剩下的进位值,这里如果有进位值只会是1 注意点: 遍历的结束条件是两方都没有值了才结束,也就是说都为 nil 一方链表为nil,就当做是 0
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func getIntersectionNode (_ headA : ListNode ?, _ headB : ListNode ?) -> ListNode ? { guard headA != nil else {return nil } guard headB != nil else {return nil } var pA: ListNode ? = headA var pB: ListNode ? = headB while pA !== pB { pA = pA == nil ? headB : pA? .next pB = pB == nil ? headA : pB? .next } return pA }
解题思路:
题目让我们找到相交节点,代表需要找到两个链表同一个对象,也就是同一个地址,但是我们需要考虑到两个链表长度是不一定一样长的,所以我们需要保证两个链表一样长,我们可以把两个链表头都保留起来,每次一方为nil就拼接另外一条链表进行遍历,这样子就能保证两条链表同时next遍历了,只要找到就跳出循环,返回pA节点。
参考如下图所示: 绿色代表不相交,红色代表相交
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例
1 2 输入:head = [1,4,3,2,5,2], x = 3 输出:[1,2,2,4,3,5]
提示
链表中节点的数目在范围 [0, 200]
内
-100 <= Node.val <= 100
-200 <= x <= 200
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func partition (_ head : ListNode ?, _ x : Int ) -> ListNode ? { guard head != nil else {return nil } var pHead = head let lHead = ListNode () var lTail = lHead let rHead = ListNode () var rTail = rHead while pHead != nil { let v = pHead? .val ?? 0 if v < x { lTail.next = pHead lTail = pHead! }else { rTail.next = pHead rTail = pHead! } pHead = pHead? .next } rTail.next = nil lTail.next = rHead.next return lHead.next }
解题思路:
使用两个虚拟节点L和R,L放小于x的节点,R放大于等于x的节点,最后把两个链表拼接起来,这里需要注意的是,rTail需要把next置空,不然可能导致后面的节点都是小于x节点的。
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表
示例
1 2 输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
1 2 3 4 5 6 7 8 9 10 11 func reverseList (_ head : ListNode ?) -> ListNode ? { var p = head var newHead:ListNode ? = nil while p != nil { let temp = p? .next p? .next = newHead newHead = p p = temp } return newHead }
解题思路:
新建一条链表,遍历原来的链表,每次把当前结点的next指向新链表,再把当前结点放到新链表头结点,一直往后遍历直到next为nil,反转完成。
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
示例
1 2 输入:head = [1,2,2,1] 输出:true
回文的意思就是左边念数字和右边念数字是一样的,也就是左右对称的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 class Solution { func isPalindrome (_ head : ListNode ?) -> Bool { if head == nil || head? .next == nil {return true } guard head? .next? .next != nil else {return head? .val == head? .next? .val} var mid = middleNode(head) var rHead = reverseList(mid? .next) var lHead = head var rOldHead = rHead var result = true while rHead != nil { if lHead? .val != rHead? .val { result = false break } rHead = rHead? .next lHead = lHead? .next } reverseList(rOldHead) return result } func middleNode (_ head :ListNode ?) -> ListNode ?{ var fast = head var slow = head while fast? .next != nil && fast? .next? .next != nil { slow = slow? .next fast = fast? .next? .next } return slow } func reverseList (_ head : ListNode ?) -> ListNode ? { var p = head var newHead:ListNode ? = nil while p != nil { let temp = p? .next p? .next = newHead newHead = p p = temp } return newHead } }
解题思路:
这里用到两个链表知识点,第一个是用快慢指针查找中间节点查找单链表的中间节点,第二个是反转单链表,只要找到中间节点之后的节点进行反转,之后遍历两个节点,都相同就是回文链表,如果想不破坏原来的链表结构,需要在结尾把反转的链表再反转回来足矣。
请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。
题目数据保证需要删除的节点 不是末尾节点 。
示例
输入:head = [4,5,1,9], node = 5 输出:[4,1,9] 解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9
提示
链表中节点的数目范围是 [2, 1000] -1000 <= Node.val <= 1000 链表中每个节点的值都是唯一的 需要删除的节点 node 是 链表中的一个有效节点 ,且 不是末尾节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { func deleteNode (_ node : ListNode ?) { node? .val = node? .next? .val ?? 0 node? .next = node? .next? .next } }
解题思路:
一般删除节点需要知道上一节点,把上一节点的next指向要删除节点的next足矣,但是这里题目只给到我们删除的节点,没法获取上一节点,那怎么办呢? 其实只要把下一个节点的值赋给当前节点值,这样子删除当前节点或者删除当前节点next节点都是一样的,[4,1,1,9], 所以我们就删除下个节点足矣。
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例
1 2 3 输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
提示
链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos
为 -1
或者链表中的一个 有效索引 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution { func hasCycle (_ head : ListNode ?) -> Bool { guard head != nil else {return false } guard head? .next != nil else {return false } var slow = head var fast = head? .next while fast != nil { if slow === fast {return true } slow = slow? .next fast = fast? .next? .next } return false } }
解题思路:
我们利用快慢指针,慢指针每次遍历只位移一次,快指针位移两次,只要有环,它们肯定会相遇,类似我们在操场跑道跑步一样,跑得快的同学,迟早会跟跑得慢的同学相遇。
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例
1 2 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]
提示
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1
和 l2
均按 非递减顺序 排列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { func mergeTwoLists (_ list1 : ListNode ?, _ list2 : ListNode ?) -> ListNode ? { guard list1 != nil else {return list2} guard list2 != nil else {return list1} var l1 = list1 var l2 = list2 let dummyHead = ListNode () var dummyTail = dummyHead while l1 != nil && l2 != nil { let v1 = l1? .val ?? 0 let v2 = l2? .val ?? 0 if v1 < v2 { dummyTail.next = ListNode (v1) l1 = l1? .next }else { dummyTail.next = ListNode (v2) l2 = l2? .next } dummyTail = dummyTail.next! } dummyTail.next = l1 == nil ? l2 : l1 return dummyHead.next } }
解题思路:
我们可以使用两个指针去遍历两个有序链表,有序这个是个关键点,每次拿到两个链表的节点依次比较添加,最后的链表肯定是有序的。 第一步: 每次遍历都判断两个指针的节点大小,添加进去的节点都需要位移,dummyTail 也要位移,最后只要有一个链表节点为nil,代表第一步合并结束, 第二步: 还需要处理一个问题,可能两个链表长度不一样,所以最后一个节点也需要添加上去。
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
提示:
k == lists.length 0 <= k <= 10^4 0 <= lists[i].length <= 500 -10^4 <= lists[i][j] <= 10^4 lists[i] 按 升序 排列 lists[i].length 的总和不超过 10^4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 func mergeKLists (_ lists : [ListNode ?]) -> ListNode ? { guard lists.count != 0 else {return nil } var dummy:ListNode ? for node in lists { dummy = mergeTwoLists(dummy,node) } return dummy } func mergeTwoLists (_ list1 : ListNode ?, _ list2 : ListNode ?) -> ListNode ? { guard list1 != nil else {return list2} guard list2 != nil else {return list1} var l1 = list1 var l2 = list2 let dummyHead = ListNode () var dummyTail = dummyHead while l1 != nil && l2 != nil { let v1 = l1? .val ?? 0 let v2 = l2? .val ?? 0 if v1 < v2 { dummyTail.next = ListNode (v1) l1 = l1? .next }else { dummyTail.next = ListNode (v2) l2 = l2? .next } dummyTail = dummyTail.next! } dummyTail.next = l1 == nil ? l2 : l1 return dummyHead.next }
第一个解题思路:(数据多的时候容易超时)
采用合并两个有序链表方法来解答,只需要在外面遍历整个数组即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 class Solution { func mergeKLists (_ lists : [ListNode ?]) -> ListNode ? { if lists.count == 0 { return nil } if lists.count == 1 { return lists[0 ] } let mid = lists.count/ 2 let left = mergeKLists(Array (lists[0 ..< mid])) let right = mergeKLists(Array (lists[mid..< lists.count])) return mergeTwoLists(left, right) } func mergeTwoLists (_ list1 : ListNode ?, _ list2 : ListNode ?) -> ListNode ? { guard list1 != nil else {return list2} guard list2 != nil else {return list1} var l1 = list1 var l2 = list2 let dummyHead = ListNode () var dummyTail = dummyHead while l1 != nil && l2 != nil { let v1 = l1? .val ?? 0 let v2 = l2? .val ?? 0 if v1 < v2 { dummyTail.next = ListNode (v1) l1 = l1? .next }else { dummyTail.next = ListNode (v2) l2 = l2? .next } dummyTail = dummyTail.next! } dummyTail.next = l1 == nil ? l2 : l1 return dummyHead.next } }
第二种解题思路:分治思想 (优)
基于前面的代码,我们应该对大问题进行分解,所以我们想到了分治思想。 分治思想就是把一个复杂的问题分成两个或更多的相同或 相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解。 我们假设 mergeKLists 函数已经实现,那么我们就可以对原数组进行分治,分开两边去处理,得到两个链表,然后我们对这两个链表进行合并即可。需要注意的是我们代码 Array(lists[0..<mid]) 需要让 mid > 0。 所以递归分治下去,数组迟早只有一份,我们返回一份即可,最终合并的时候都只会是两个链表进行合并,达到用需要解决的问题来解决自己本身,是不是很有code魅力!