面试算法2---链表问题(1)


0、节点定义

//单链表
class Node {
	public int value;
	public Node next;
	public Node(int value) {
		this.value=value;
	}
}

//双链表
class DoubleNode{
	public int value;
	public DoubleNode last;
	public DoubleNode next;
	
	public DoubleNode(int data) {
		this.value=data;
	}
}

一、打印两个有序链表的公共部分

给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。

//打印两个有序链表的公共部分
public void printCommonPart(Node head1, Node head2) 
{
	System.out.println("Common Part:");
	if(head1==null || head2==null) {
		return ;
	}
	while(head1!=null && head2!=null) {
		if (head1.value==head2.value) {
			System.out.print(head2.value + " ");
			head1=head1.next;
			head2=head2.next;
		}else if (head1.value>head2.value){
			head2=head2.next;
		}else {
			head1=head1.next;
		}
	}
	System.out.println();
}

二、在单链表和双链表中删除倒数第K个节点

如果链表的长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。

//单链表中删除倒数第K个节点
public Node deletelastKthNode(Node head,int lastKth) {
	// 若不需要删除,则返回头结点
	if (head==null || lastKth<1) {
		return head;
	}
	Node cur=head;
	while(cur!=null) {
		lastKth--;
	}
	if (lastKth==0) {
		return head.next;
	}

	if (lastKth==0) {
		return head.next;
	}
	if (lastKth<0) {
		cur=head;
		while(++lastKth!=0) {
			cur=cur.next;
		}
		cur=cur.next;
	}
	return head;
}

//双链表中删除倒数第K个节点
public DoubleNode deleteLastkthNode(DoubleNode head, int lastKth) {
	if(head==null || lastKth<1) {
		return head;
	}
	DoubleNode cur=head;
	while(cur!=null) {
		cur=cur.next;
		lastKth--;
	}
	if (lastKth==0) {
		head = head.next;
		head.last=null;
	}
	
	if (lastKth<0) {
		cur=head;
		while(++lastKth!=0) {
			cur=cur.next;
		}
		DoubleNode next = cur.next.next;
		cur.next=next;
		//next.last=cur;
		if(next!=null) {
			next.last=cur;
		}
	}
	
	return head;
}

三、删除链表的中间节点

给定链表的头结点head,实现删除链表的中间节点的函数。
如:
不删除任何节点;
1->2,删除节点1;
1->2->3,删除节点2
1->2->3->4,删除节点2;
1->2->3->4->5,删除节点3;

// 删单链表的中间节点
public Node removeMidNode(Node head) {
	//无节点和只有一个节点时,不删
	if (head==null || head.next==null) {
		return head;
	}
	// 2个节点
	if(head.next!=null && head.next.next==null) {
		return head.next;
	}
	Node pre=head;
	Node cur=head.next.next;
	while(cur.next!=null && cur.next.next!=null) {
		pre=pre.next;
		cur=cur.next.next;
	}
	pre.next=pre.next.next;
	return head;
}

四、反转单向和双向链表

最好能做到代码一次成型,运行不出错。

// 反转单链表
public Node reverseNode(Node head) {
	if (head==null) {
		return head;
	}
	Node pre=null;
	Node next=null;
	while(head!=null) {
		next=head.next;
		head.next=pre;
		pre=head;
		head=next;
	}
	return pre;
}

// 反转双链表
public DoubleNode reverseDoubleNode(DoubleNode head) {
	if (head==null) {
		return head;
	}
	DoubleNode pre=null;
	DoubleNode next=null;
	
	while(head!=null) {
		next=head.next;
		head.next=pre;
		head.last=next;
		pre=head;
		head=next;
	}
	return pre;
}

五、反转部分单向链表

给定一个单向链表的头节点head,以及两个整数from和to,在单向链表上把第from个节点到第to个节点这一部分进行反转。
例如:
1->2->3->4->5->null,from=2,to=4
调整结果为:1->4->3->2->5->null
再如:
1->2->3->null, from=1, to=3
调整结果为:3->2->1->null

// 部分反转单链表
public Node reversePart(Node head,int from,int to) {
	if (head==null) {
		return head;
	}
	Node node1=head;
	int len=0;
	Node fPre=null;
	Node toPos=null;
	while(node1!=null) {
		len++;
		fPre=(len==from-1?node1:fPre);
		toPos=(len==to+1?node1:toPos);
		node1=node1.next;
	}
	if(from>=to || from<1 || to>len) {
		return head;
	}
	node1 =(fPre==null?head:fPre.next);
	Node node2=node1.next;
	node1.next=toPos;
	Node next=null;
	while(node2!=toPos) {
		next=node2.next;
		node2.next=node1;
		node1=node2;
		node2=next;
	}
	if(fPre!=null) {
		fPre.next=node1;
		return head;
	}
	return node1;
}

六、判断一个链表是否为回文结构

给定一个链表的头节点head,请判断该链表是否为回文结构。
例如:
1->2->1->null,返回true;
1->2->2->1->null,返回true;
1->2->3->null,返回false;

// 利用栈结构,判断链表是否为回文结构
public boolean isPalindromel(Node head) {
	Stack<Node> stack = new Stack<Node>();
	Node cur =head;
	while(cur!=null) {
		stack.push(cur);
		cur=cur.next;
	}
	cur=head;
	while(cur!=null) {
		if(stack.pop().value != cur.value) {
			return false;
		}
		cur=cur.next;
		
	}
	return true;
}

七、将单向链表按某值划分成左边小、中间相等、右边大的形式

给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的函数,将链表调整为左部分都是小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。除了这个要求外,对调整后的节点顺序没有更多的要求。

例如:链表9->0->4->5->1,pivot=3.
调整后链表可以是1->0->4->9->5,也可以是:0->1->9->5->4。

//单向链表按某值划分
public Node listPartition1(Node head, int pivot) {
	if (head==null) {
		return head;
	}
	int k=0;
	Node cur=head;
	while(cur!=null) {
		k++;
		cur=cur.next;
	}
	Node[] nodeArr = new Node[k];
	cur=head;
	k=0;
	while(cur!=null) {
		nodeArr[k++]=cur;
		cur=cur.next;
	}
	// 数组内元素调整
	arrPartition(nodeArr, pivot);
	// 数组拼接成链表
	for(k=1; k < nodeArr.length; k++) {
		nodeArr[k-1].next=nodeArr[k];
	}
	nodeArr[k-1].next = null;
	
	return nodeArr[0];
}

public void swapNode(Node[] nodeArr, int a,int b) {
	Node temp = nodeArr[a];
	nodeArr[a]=nodeArr[b];
	nodeArr[b]=temp;
}

public void arrPartition(Node[] nodeArr, int pivot) {
	int small=-1;
	int len=nodeArr.length;
	int index=0;
	while(index!=len) {
		if(nodeArr[index].value<pivot) {
			swapNode(nodeArr, ++small, index++);
		}else if (nodeArr[index].value == pivot) {
			index++;
		}else {
			swapNode(nodeArr, --len, index);
		}
	}
}

八、两个单链表生成相加链表

假设链表中每个节点的值都在0~9之间,那么链表整体就可以代表一个整数。例如:9->3->7,可以代表整数937。
例如:链表1位9->3->7,链表2为6->3,最后生成新的结果链表为1->0->0->0。

思路:先将链表转换成整数,求和,再转换成链表。这种方式转int类型时可能会溢出,所以不建议这种方法。

//利用栈结构求解。该方法空间复杂度较高,可以利用逆序链表求解
public  Node addList(Node head1,Node head2) {
	Stack<Integer> s1 = new Stack<Integer>();
	Stack<Integer> s2 = new Stack<Integer>();
	Node cur=head1;
	while(cur!=null) {
		s1.push(cur.value);
		cur=cur.next;
	}
	cur=head2;
	while(cur!=null) {
		s2.push(cur.value);
		cur=cur.next;
	}
	Node node=null;
	Node pre=null;
	int ca=0;
	int n=0;
	int n1=0;
	int n2=0;
	while(!s1.isEmpty() || !s2.isEmpty()) {
		n1= (s1.isEmpty())?0:s1.pop();
		n2= (s2.isEmpty())?0:s2.pop();
		n=n1+n2+ca;
		pre=node;
		node = new Node(n%10);
		node.next=pre;
		ca=n/10;
	}
	if(ca==1) {
		pre=node;
		node=new Node(1);
		node.next=pre;
	}
	return node;
}

九、将单链表的每K个节点逆序

给定一个单链表的头节点head,实现一个调整单链表的函数,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。
例如:
1->2->3->4->5->6->7->8->null,K=3。
调整后:3->2->1->6->5->4->7->8->null。其中7/8不调整,因为不够一组。

//将单链表的每K个节点之间逆序
public Node reverseKNodes(Node head, int K) {
	if (K<2) {
		return head;
	}
	Stack<Node> stack = new Stack<Node>();
	
	Node cur = head;
	Node pre = null;
	Node next = null;
	Node newHead = head;
	while (cur!=null) {
		next = cur.next;
		stack.push(cur);
		if (stack.size() == K) {
			pre = reverse(stack, pre, next);
			newHead = newHead==head?cur:newHead;
		}
		cur = next;
	}
	return newHead;
}

public Node reverse(Stack<Node> stack, Node left, Node right) {
	Node cur = stack.pop();
	if(left != null) {
		left.next=cur;
	}
	Node next = null;
	while (!stack.isEmpty()) {
		next = stack.pop();
		cur.next = next;
		cur = next;
	}
	cur.next = right;
	return cur;
}

十、删除无序单链表中值重复出现的节点

给定一个无序单链表的头节点head,删除其中值重复出现的节点。
例如:1->2->3->3->4->4->2->1->1->null,删除值重复的节点之后为1->2->3->4->null。
方法一:时间复杂度为O(1)。
方法二:额外空间复杂度为O(1)。

//方法一:删除无序单链表中值重复出现的节点
public void removeRep1(Node head) {
	if(head == null) {
		return;
	}
	Node pre = head;
	Node cur = pre.next;
	HashSet<Integer> set = new HashSet<>();
	set.add(head.value);
	while(cur!=null) {
		if(set.contains(cur.value)) {
			pre.next = cur.next;
		} else {
			set.add(cur.value);
			pre = cur;
		}
		cur = cur.next;
	}
}
//方法二:删除无序单链表中值重复出现的节点
public void removeRep2(Node head) {
	Node cur = head;
	Node pre = null;
	Node next = null;
	while(cur!=null) {
		pre = cur;
		next = pre.next;
		while(next!=null) {
			if(cur.value == next.value) {
				pre.next = next.next;
			}else {
				pre = next;
			}
			next = next.next;
		}
		cur = cur.next;
	}
}
GNG
发布了118 篇原创文章 · 获赞 389 · 访问量 68万+

猜你喜欢

转载自blog.csdn.net/so_geili/article/details/100620049