Single linked list related operations

Implementing Inversion of Single List

1. The simplest and most violent method is to traverse the list and create nodes with header interpolation. The time complexity is O (n), and space needs to create n nodes.

2. Using pointer steering method
We know that a single list can only be traversed in one-way order, not backward. Then, how to realize the inversion of the single linked list, we can divide the list into two separate parts by reference (pointer). The first part is the list that has been in reverse order, and the second part is the part that has not been traversed. Because of the unidirectionality of the pointer, we must ensure that both parts have pointer pointers, otherwise the list will be lost.
The cur pointer is used to point to the node being traversed (Part II), while preNode always points to the head node of Part I, because this allows cur to connect to the preNode node.
nextNode always points to the first node of the second part, which ensures that the second part is not lost, and allows cur to point to the past.
Is it a bit like direct insertion sort, but also divided into two parts, the first part has been sorted, and the latter part is to be traversed?
The code is as follows

public void RecverseIteratively(Node head) {
		Node cur = head;
		Node nextNode = null;
		Node preNode = null;
		while(cur!=null) {
			nextNode = cur.next;
			if(nextNode == null) {//The last node that indicates cur points to is the head node of the list that needs to be reverse-ordered, so it needs to be pointed to by the head node.
				head = cur;
			}
			cur.next = preNode;
			preNode = cur;
			cur = nextNode;
		}
	}`

Quick and Slow Pointer Reference in Link List

Because of the nature of single linked list, one-way sequential traversal, and we can easily handle some problems with two pointers (one running fast, one running slowly, the minute hand and the second hand of the reference clock).
1. Detecting whether there are rings in the list
This is a classic problem solved with fast and slow pointers. If there is a loop, the slow pointer will meet with it before it traverses the list (coincidence of minutes and seconds of the reference clock). If there is no loop, the fast pointer will reach the end first. Therefore, the cycle condition only uses the fast pointer.
The code is as follows:

public boolean isLoop(Node head) {
		Node fastNode = head;
		Node slowNode = head;
		while(fastNode!=null&&fastNode.next!=null) {
			//fastNode.next!=null is designed to avoid null pointer exceptions
			fastNode = fastNode.next.next;
			slowNode = slowNode.next;
			if(fastNode == slowNode) {//If there is a ring, then the speed pointer will definitely meet, because the speed is different, like a clock, the second hand will always meet the minute hand.
				return true;
			}
		}
			return false;
	}

2. Find the intermediate nodes of single linked list
The simplest way is to traverse one side of the list and find the length of the list. The second time, it's only half way through the list, but it takes two times to traverse the list, which is a bit expensive.
Then we look for a way to find the intermediate node by traversing it. That's right. It's a fast or slow pointer.
Similarly, the fast pointer takes two steps and the slow pointer takes one step. We find that if the chain length is odd, the fast pointer goes to the end, and the slow pointer is just in the middle. When the linked list length is even, when the fast pointer reaches the penultimate node, the slow pointer points to the intermediate node and the next node (even intermediate value has two);
The code is as follows:

public Node SearchMid(Node head) {
		Node slowNode = head;
		Node fastNode = head;
		while(fastNode!=null&&fastNode.next!=null&&fastNode.next.next!=null) {//There are three conditions for this judgment and they are all short-circuited, so there is no null pointer exception.
			//fastNode!=null is used to determine whether it is an empty list, and if so, return empty?
			//When fastNode.next!=null is used to determine whether fastNode is at the tail node or not.
			//When fastNode.next.next!=null is used to determine whether fastNode is the penultimate node or not.
			//These are all cyclic termination conditions, at which time slowNode is already in the middle node position
			fastNode = fastNode.next.next;
			slowNode = slowNode.next;
		}
		return slowNode;
	}

3. Find the reciprocal k element in a single list
1. Similarly, the simple violence method is to traverse one side list to find its length n, and then cycle to n-k, which also needs to traverse twice.
2. Optimize it. Let's traverse k nodes from the current node each time to see if the end point is reached. If the end point is reached, the current node is the penultimate k node. The code is as follows:

public Node findElem(int k) {
		if(k<1) return null;
		Node cur = head;
		Node nextNode = null;
		while(cur!=null) {
			nextNode = cur;
			for(int i = 1; i<=k; i++) {
				nextNode = nextNode.next;
			}
			if(nextNode==null) {
				break;
			}
			cur = cur.next;
			
		}
		return cur;
	}

3. Continue to optimize, we found that method 2 has a disadvantage, that is, internal loops, so the time complexity is O (nk) is basically n_level, so we use fast and slow pointer method.
Of course, the fast and slow pointers here only indicate the relative position of the two pointers, not the speed of the pointer movement. In this way, the fast pointer moves to the k first position, and then the fast and slow pointers move together. If the fast pointer reaches the end, then the slow pointer points to the penultimate k node. The code is as follows:

public Node findElem1(int k) {
		if(k<1) return null;
		Node cur = head;
		Node nextNode = head;
		for(int i = 1; i<k&&nextNode!=null; i++) {//Also here's nextNode! = null is also to avoid null pointer exceptions, because we do not know the length of the list, it is possible that k is longer than the length of the list.
			nextNode = nextNode.next;
		}
		if(nextNode == null){
			throw new Exception("k Wrongful");
		}
		while(nextNode.next!=null) {//Next, the two pointers moved together
			cur = cur.next;
			nextNode = nextNode.next;
		}
		return cur;
	}

End-to-End Output Single-Linked List

Obviously, a single list can only be traversed sequentially, how to output in reverse, the answer is ready to come out, yes, is to use the stack. Now that stacks are used, we can solve this problem recursively in a simpler way.
Recursive Termination Conditions: Recursive to Empty Nodes
 Recursion: The parameter condition is cur.next;
Return: When we return, we can just output the node directly.
The code is as follows:
public void printListReversely(Node head) {
		if(head!=null) {//Recursive Critical Conditions
			printListReversely(head.next);//Pass to
			System.out.println(head.val);//Return
		}
	}

Of course, we can also use the stack to solve this problem. Without traversing a node, we can put its value on the stack. After traversing, we can go out of the stack in turn.
The code is as follows:

public void printListReversely(Node head) {
		Stack<Integer> stack = new Stack<>();
		Node cur = head;
		while(cur!=null) {
			stack.push(cur.val);
			cur = cur.next;
		}
		while(!stack.empty()) {
			System.out.println(stack.pop());
		}
	}

Delete the specified node and the header node is unknown

If the head node knows, it's very simple for us to traverse to the front of the specified node location, and then delete it. However, the head node is unknown, so we can not find the precursor of the node. So we can't delete them in general way. We find that if we change the values of two nodes, can we simply think that two nodes have changed? Yes, that's the way we think. Since the pioneers can not be found, we can create our own pioneers. How to create it? We will exchange the value of the specified node with the next node, so we can think that the node to be deleted "runs behind", and the node we point to is the precursor of the node to be deleted. Of course, as can be seen from the above, if we want to delete the endpoint, we can not. Because we can't find a precursor node.
The code is as follows:
public boolean deleteNode(Node cur) {
		if(cur==null||cur.next==null) {
			return false;
			//cur.next==null means that if it is a tail node, it cannot be deleted because the precursor node does not know.
		}
		//If it is a non-endpoint, then we can exchange the value of the node to be deleted with the value of the next node, so deleting the next node is equivalent to deleting the node.
		cur.val = cur.next.val;//Here I do not exchange, as long as the value of the latter node can be saved, as to delete the value of the node does not matter.
		cur.next = cur.next.next;
		return true;
	}

Judging whether two linked lists intersect

We know that a single linked list has only one pointer field, which means that if two linked lists intersect, then all the nodes after the intersection point are common, then we can traverse to the tail node. If two linked lists intersect, then the endpoint that must be pointed to is the same.
The code is as follows:
public boolean isIntersect(Node h1,Node h2) {
		if(h1==null||h2==null) {
			return false;  //If any list is empty, then you must not want to hand it in.
		}
		Node p = h1;
		Node q = h2;
		while(p.next!=null) {
			p = p.next;
		}
		while(q.next!=null) {
			q = q.next;
		}
		return p==q;

Delete duplicate elements from the list

1. A simple method, we can use the nature of set set set set, that is, no repetition, every add node, to see whether the join is successful, success statement is not repetition, failure statement is repetition, deletion can be done.
The code is as follows:

public void deleteDuplecate(Node head) {
		Set<Node> set = new HashSet<Node>();
		Node preNode = null;
		Node cur = head;
		while(cur!=null) {
			if(set.add(cur)) {
				preNode = cur;
			}else {
				preNode.next = cur.next;
			}
			cur = cur.next;
		}
	}

2. Method 1 is simple, but it has extra space overhead by using set set set set. We can also delete duplicate elements by changing time and space. How to do this? When we traverse a node through double loops, the inner loop traverses from the next node of the current node to the tail node, and deletes duplicate elements. The code is as follows:

public void deleteDuplecate1(Node head) {
		Node cur = head;
		Node preNode = null;//Point to the precursor of the node to be deleted
		Node nextNode = null;
		while(cur!=null) {
			preNode = cur;
			nextNode = cur.next;
			while(nextNode!=null) {
				if(cur.val==nextNode.val) {
					preNode.next = nextNode.next;
					
				}else {
					preNode = preNode.next;
				}
				nextNode = nextNode.next;
			}
			cur = cur.next;
		}
	}

Posted by Guldstrand on Fri, 10 May 2019 23:31:21 -0700