You đang tìm kiếm từ khóa Linked list techniques được Cập Nhật vào lúc : 2022-03-14 09:10:23 . Với phương châm chia sẻ Mẹo về trong nội dung bài viết một cách Chi Tiết Mới Nhất. Nếu sau khi tìm hiểu thêm Post vẫn ko hiểu thì hoàn toàn có thể lại phản hồi ở cuối bài để Admin lý giải và hướng dẫn lại nha.
In this chapter, you’ll work through five common scenarios for the linked list. These problems are relatively easy compared to most challenges, and they will serve to solidify your knowledge of data structures.
Nội dung chính
Open the starter project to begin. In it, you’ll find the following challenges.
Create a function that prints the nodes of a linked list in reverse order. For example:
1 -> 2 -> 3 -> nil
// should print out the following:
3
2
1
Create a function that finds the middle node of a linked list. For example:
1 -> 2 -> 3 -> 4 -> nil
// middle is 3
1 -> 2 -> 3 -> nil
// middle is 2
Create a function that reverses a linked list. You do this by manipulating the nodes so that they’re linked in the other direction. For example:
// before
1 -> 2 -> 3 -> nil
// after
3 -> 2 -> 1 -> nil
Create a function that takes two sorted linked lists and merges them into a single sorted linked list. Your goal is to return a new linked list that contains the nodes from two lists in sorted order. You may assume the sort order is ascending. For example:
// list1
1 -> 4 -> 10 -> 11
// list2
-1 -> 2 -> 3 -> 6
// merged list
-1 -> 1 -> 2 -> 3 -> 4 -> 6 -> 10 -> 11
Create a function that removes all occurrences of a specific element from a linked list. The implementation is similar to the remove(:) method that you implemented for the linked list. For example:
// original list
1 -> 3 -> 3 -> 3 -> 4
// list after removing all occurrences of 3
1 -> 4
A straightforward way to solve this problem is to use recursion. Since recursion allows you to build a call stack, you just need to call the print statements as the call stack unwinds.
Your first task is to recursively traverse to the end. Add the following helper function to your playground:
private func printInReverse(_ node: Node?)
// 1
guard let node = node else return
// 2
printInReverse(node.next)
You first start off with the base case: the condition to terminating the recursion. If node is nil, then it means you’ve reached the end of the list.
This is your recursive call, calling the same function with the next node.
Printing
Where you add the print statement will determine whether you print the list in reverse order or not.
Update the function to the following:
private func printInReverse(_ node: Node?)
guard let node = node else return
printInReverse(node.next)
print(node.value)
Any code that comes after the recursive call is called only after the base case triggers (i.e. after the recursive function hits the end of the list). As the recursive statements unravel, the node data gets printed out.
Finally, you need to call the helper method from the original printInReverse function. Update that to look like this:
func printInReverse(_ list: LinkedList)
printInReverse(list.head)
Test it out!
Write the following the bottom of the playground page:
example(of: “printing in reverse”)
var list = LinkedList()
list.push(3)
list.push(2)
list.push(1)
print(“Original list: (list)”)
print(“Printing in reverse:”)
printInReverse(list)
You should see the following output:
—Example of printing in reverse—
Original list: 1 -> 2 -> 3
Printing in reverse:
3
2
1
The time complexity of this algorithm is O(n), since you have to traverse each node of the list. The space complexity is likewise O(n) since you are implicitly using the function call stack to process each element.
One solution is to have two references traverse down the nodes of the list where one is twice as fast as the other. Once the faster reference reaches the end, the slower reference will be in the middle. Update the function to the following:
func getMiddle(_ list: LinkedList) -> Node?
var slow = list.head
var fast = list.head
while let nextFast = fast?.next
fast = nextFast.next
slow = slow?.next
return slow
In the while declaration, you bind the next node to nextFast. If there is a next node, you update fast to the next node of nextFast, effectively traversing the list twice. The slow pointer is updated only once. This is known as the runner’s technique.
Try it out!
Write the following the bottom of the playground page:
example(of: “getting the middle node”)
var list = LinkedList()
list.push(3)
list.push(2)
list.push(1)
print(list)
if let middleNode = getMiddle(list)
print(middleNode)
You should see the following output:
—Example of getting the middle node—
1 -> 2 -> 3
2 -> 3
The time complexity of this algorithm is O(n), since you traversed the list in a single pass. The runner’s technique is helpful in solving a variety of problems associated with the linked list.
To reverse a linked list, you need to visit each node and update the next reference to point in the other direction. This can be a tricky task, since you’ll need to manage multiple references to multiple nodes.
The easy way
You can trivially reverse a list by using the push method along with a new temporary list. Update the code in the playground:
extension LinkedList
mutating func reverse()
// 1
let tmpList = LinkedList()
for value in self
tmpList.push(value)
// 2
head = tmpList.head
You first start by pushing the current values in your list to a new tmpList. This will create a list in reverse order.
You point the head of the list to the reversed nodes.
O(n) time complexity, short and sweet!
But wait…
Although O(n) is the optimal time complexity for reversing a list, there’s a significant resource cost in the previous solution. As it is now, reverse will have to allocate new nodes for each push method on the temporary list. You can avoid using the temporary list entirely, and reverse the list by manipulating the next pointers of each node. The code ends up being more complicated, but you reap considerable benefits in terms of performance.
Update the reverse method to the following:
mutating func reverse()
tail = head
var prev = head
var current = head?.next
prev?.next = nil
// more to come…
You begin by assigning head to tail. Next, you create two references — prev and current — to keep track of traversal. The strategy is fairly straightforward: each node points to the next node down the list. You’ll traverse the list and make each node point to the previous node instead:
As you can see from the diagram, it gets a little tricky. By pointing current to prev, you’ve lost the link to the rest of the list. Therefore, you’ll need to manage a third pointer. Add the following the bottom of the reverse method:
while current != nil
let next = current?.next
current?.next = prev
prev = current
current = next
Each time you perform the reversal, you create a new reference to the next node. After every reversal procedure, you move the two pointers to the next two nodes.
Once you’ve finished reversing all the pointers, you’ll set the head to the last node of this list. Add the following the end of the reverse method:
head = prev
Try it out!
Test the reverse method by writing the following the bottom of the playground page:
example(of: “reversing a list”)
var list = LinkedList()
list.push(3)
list.push(2)
list.push(1)
print(“Original list: (list)”)
list.reverse()
print(“Reversed list: (list)”)
You should see the following output:
—Example of reversing a list—
Original list: 1 -> 2 -> 3
Reversed list: 3 -> 2 -> 1
The time complexity of your new reverse method is still O(n), the same as the trivial implementation discussed earlier. However, you didn’t need to use a temporary list or allocate new Node objects, which significantly improves the performance of this algorithm.
The solution to this problem is to continuously pluck nodes from the two sorted lists and add them to a new list. Since the two lists are sorted, you can compare the next node of both lists to see which one should be the next one to add to the new list.
Setting up
You’ll begin by checking the cases where one or both of the lists are empty. Add the following to mergeSorted:
guard !left.isEmpty else
return right
guard !right.isEmpty else
return left
var newHead: Node?
If one is empty, you return the other. You also introduce a new reference to hold a sorted list of Node objects. The strategy is to merge the nodes in left and right onto newHead in sorted order.
Write the following below newHead:
// 1
var tail: Node?
var currentLeft = left.head
var currentRight = right.head
// 2
if let leftNode = currentLeft, let rightNode = currentRight
if leftNode.value < rightNode.value
newHead = leftNode
currentLeft = leftNode.next
else
newHead = rightNode
currentRight = rightNode.next
tail = newHead
You create a pointer to the tail of the new list you’re adding to. This allows for constant-time append operations.
You compare the first nodes of left and right to assign newHead.
Merging
Next, you’ll need to iterate through both left and right, cherry picking the nodes to add to ensure that the new list is sorted. Add the following to the end of the function:
// 1
while let leftNode = currentLeft, let rightNode = currentRight
// 2
if leftNode.value < rightNode.value
tail?.next = leftNode
currentLeft = leftNode.next
else
tail?.next = rightNode
currentRight = rightNode.next
tail = tail?.next
The while loop will continue until one of the list reaches the end.
Much like before, you do a comparison on the nodes to find out which node to connect to tail.
Since this loop depends on both currentLeft and currentRight, it will terminate even if there are nodes left on either list.
Add the following to handle the remaining nodes:
if let leftNodes = currentLeft
tail?.next = leftNodes
if let rightNodes = currentRight
tail?.next = rightNodes
This appends the remainder of the nodes.
To wrap things up, you instantiate a new list. Instead of using using the append or insert methods to insert elements to the list, you’ll simply set the reference of the head and tail of the list directly:
var list = LinkedList()
list.head = newHead
list.tail =
while let next = tail?.next
tail = next
return tail
()
return list
Try it out!
Write the following the bottom of the playground:
example(of: “merging two lists”)
var list = LinkedList()
list.push(3)
list.push(2)
list.push(1)
var anotherList = LinkedList()
anotherList.push(-1)
anotherList.push(-2)
anotherList.push(-3)
print(“First list: (list)”)
print(“Second list: (anotherList)”)
let mergedList = mergeSorted(list, anotherList)
print(“Merged list: (mergedList)”)
You should see the following output:
—Example of merging two lists—
First list: 1 -> 2 -> 3
Second list: -3 -> -2 -> -1
Merged list: -3 -> -2 -> -1 -> 1 -> 2 -> 3
This algorithm has a time complexity of O(m + n), where m is the # of nodes in the first list, and n is the # of nodes in the second list.
The goal of this solution is to traverse down the list, removing all nodes that matches the element you want to remove. Each time you perform a removal, you need to reconnect the predecessor node with the successor node. While this can get complicated, it’s well worth it to practice this technique. Many data structures and algorithms will rely on clever uses of pointer arithmetic to build.
There are a few cases you need to consider. The first is to clear out the nodes from the front of the list.
Trimming the head
The first case to consider is when the head of the list contains the value that you want to remove. Suppose you want to remove 1 from the following list:
You’d want your new head to point to 2.
Write the following inside the remove function:
while let head = head, head.value == value
head = head?.next
You first giảm giá with the case where the head of the list contains the value you want to remove. Since it’s possible to have a sequence of nodes with the same value, you use a while loop to ensure that you remove them all.
Unlinking the nodes
Like many of the algorithms associated with the linked list, you’ll be leveraging your pointer arithmetic skills to unlink the nodes. Write the following the bottom of remove:
var prev = head
var current = head?.next
while let currentNode = current
guard currentNode.value != value else
prev?.next = currentNode.next
current = prev?.next
continue
// more to come
You’ll need to traverse the list using two pointers. The else block of the guard statement will trigger if it’s necessary to remove the node.
You modify the list so that you bypass the node you don’t want:
Keep traveling…
Can you tell what’s missing? As it is right now, the while statement may never terminate. You need to move the prev and current pointers along. Write the following the bottom of the while loop, after the guard statement:
prev = current
current = current?.next
Finally, you’ll update the tail of the linked list. This is necessary in the case where the original tail is a node containing the value you wanted to remove. Add the following to the end of removeAll:
tail = prev
And that’s it for the implementation!
Try it out!
Write the following the bottom of the playground page:
example(of: “deleting duplicate nodes”)
var list = LinkedList()
list.push(3)
list.push(2)
list.push(2)
list.push(1)
list.push(1)
list.removeAll(3)
print(list)
You should see the following output:
—Example of deleting duplicate nodes—
1 -> 1 -> 2 -> 2
This algorithm has a time complexity of O(n), since you’ll need to go through all the elements.
Bạn vừa đọc nội dung bài viết Với Một số hướng dẫn một cách rõ ràng hơn về Clip Linked list techniques tiên tiến và phát triển nhất
Hero đang tìm một số trong những Chia Sẻ Link Down Linked list techniques miễn phí.
Nếu sau khi đọc nội dung bài viết Linked list techniques vẫn chưa hiểu thì hoàn toàn có thể lại Comment ở cuối bài để Ad lý giải và hướng dẫn lại nha
#Linked #list #techniques
Tra Cứu Mã Số Thuế MST KHƯƠNG VĂN THUẤN Của Ai, Công Ty Doanh Nghiệp…
Các bạn cho mình hỏi với tự nhiên trong ĐT mình gần đây có Sim…
Thủ Thuật về Nhận định về nét trẻ trung trong môi trường tự nhiên vạn…
Thủ Thuật về dooshku là gì - Nghĩa của từ dooshku -Thủ Thuật Mới 2022…
Kinh Nghiệm Hướng dẫn Tìm 4 số hạng liên tục của một cấp số cộng…
Mẹo Hướng dẫn Em hãy cho biết thêm thêm nếu đèn huỳnh quang không còn…