链表问题总结

  • 链表

  链表是一种基本的一维离散存储数据结构。相对于数组,它是离散内存的,不能如数组一样通过下标来查询相应地数据,每一个链表节点只能知道它的上一个(双端链表)和它的下一个(单链表,双端链表)节点。C语言通过malloc/free控制内存,C++里通过new/delete,Java则是只有new对象。

  1.Java链表节点定义

class Node{
	private Node next;
	private int data;
	public Node(){
		
	}
	public Node(int data){	
		this.data=data;
	}
	public int getData() {
		return data;
	}

	public void setData(int data) {
		this.data = data;
	}

	public Node getNext() {
		return next;
	}

	public void setNext(Node next) {
		this.next = next;
	}
	
}

  2.链表操作类

class LinkedList{
	private Node first;

	public Node getfirst() {
		return first;
	}

	public void setfirst(Node first) {
		this.first = first;
	}
	//按值搜寻第一个等于目标的节点
	public Node findNode(int target){
		if(first==null){
			System.out.println("initialise the first");
			return null;
		}
		Node p=first;
		while(p.getData()!=target && p!=null)
			p=p.getNext();
		return p;
	}
	public Node findPreNode(Node target){
		if(first==null){
			System.out.println("initialise the first");
			return null;
		}
		Node p=first;
		while(p.getNext()!=target)
			p=p.getNext();
		return p;
	}
	//返回链表下标查找
	public int IndexOfNode(int target){
		int index=1;
		if(first==null){
			System.out.println("initialise the first");
			return -1;
		}
		Node p=first;
		while(p.getData()!=target)
		{
			index++;
			p=p.getNext();
			if(p==null)
			{
				index=-1;
				break;
			}
		}
		return index;
	}
	//在末尾添加节点
	public void addNode(Node add){
		if(first==null){
			first=add;
			add.setNext(null);
			return;
		}
		Node p=first;
		while(p.getNext()!=null)
			p=p.getNext();
		p.setNext(add);
		add.setNext(null);
	}
	//头插法生成链表
	public void headAdd(Node node){
		if(first == null){
			first=node;
			node.setNext(null);
		}else{
			node.setNext(first);
			first=node;
		}
	}
	//删除节点--须指定链表中的节点
	public void deleteNode(Node toDel){
		if(first==null){
			System.out.println("initialise the first");
			return ;
		}
		Node p=toDel.getNext();
		if(p!=null)
		{
			toDel.setNext(p.getNext());		
			toDel.setData(p.getData());
			p.setNext(null);
		}else{
			deleteEndNode(toDel);
		}
	}
	//删除末尾
	public void deleteEndNode(Node toDel){
		Node p=first;
		while(p.getNext()!=toDel && p!=null)
			p=p.getNext();
		
		p.setNext(null);
		
		
	}
	//常规删除
	public void deleteNormal(Node toDel){
		Node p=findPreNode(toDel);
		p.setNext(toDel.getNext());
		toDel.setNext(null);
	}
	//修改一个节点的值
	public void update(Node target,int value){
		if(first==null){
			System.out.println("initialise the first");
			return ;
		}
		target.setData(value);
	}
	public void printList(){
		if(first==null){
			System.out.println("initialise the first");
			return ;
		}
		Node p=first;
		while(p!=null){
			System.out.print(p.getData()+" ");
			p=p.getNext();
		}
		System.out.println();
	}
}

  小记:(1) 生成单链表可以有头插法和尾插法,尾插法的添加节点为O(n)复杂,删除也是O(n);头插法添加则是O(1),删除还是O(n)。

       (2) 删除方法有一个是O(1)复杂度的。方法:给定一个节点,删除它下一个节点并把下一个节点的值转移到给定节点。

  • 问题1:删除链表内重复的节点。(数据重复)

    因为使用的是那个O(1)删除的方法,所以写得有些奇怪。总体方法就是使用一个O(n)空间来存储节点,发现重复就删除,遍历时间复杂就只用O(n)。

//删除未排序链表重复节点1 缓冲区版
	public void DelRepeated1(LinkedList list){
		HashMap<Integer,Boolean> buffer=new HashMap<Integer,Boolean>();
		Node p=list.getfirst();
		while(p!=null){
			System.out.println("data="+p.getData());
			if(buffer.get(p.getData())==null){
				buffer.put(p.getData(), true);
			}
			else
			{
				list.deleteNode(p);	
				while(true){
					if(buffer.get(p.getData())!=null)
						list.deleteNode(p);
					else
						break;
				}
			}
			
			p=p.getNext();
		}
	}

    若不使用缓冲区,则只能使用暴力遍历法,从当前节点开始到表尾“排除异己”,“步步为营”。复杂度O(n*n)。

    并且删除方法是使用O(n)复杂度的。

	//删除未排序俩表重复节点2 无缓冲区
	public void DelRepeated2(LinkedList list) {
		Node p = list.getfirst();
		Node q = list.getfirst().getNext();
		while(p!=null){
			q = p.getNext();
			while(q!=null){
				if(p.getData()==q.getData())
					list.deleteNormal(q);
				q=q.getNext();
			}
				p = p.getNext();

		}
		
	}
  • 问题2:寻找倒数低k个节点。

    这个问题其实挺简单,如果链表是你自建的,那么添加个length或size属性记录那么解决这个问题就方便多了。如果仅给定一个单链表,则遍历一次找出length再以互补或互余的思维来求出倒k的下标。

//寻找倒数第k个节点
	public Node findNK(LinkedList list,int k){
		if(k<=0)
		{
			System.out.println("k should be upper than 0");
			return null;
		}
		int n=0;
		Node p=list.getfirst();
		while(p!=null)
		{
			n++;
			p=p.getNext();
		}
		int result=n-k;
		if(n-k<0)
		{
			System.out.println("index out of range");
			return null;
		}
		p=list.getfirst();
		while(result!=0){
			p=p.getNext();
			result--;
		}
		return p;
	}
  • 问题3:给定一个值x,将链表分割成两部分,大于x部分和小与x部分。

    接到这道题的时候,我马上蹦出一个想法:排序。排完序就解决了。最快的排序算法也是O(n*logn)。然后想到快速排序,快速排序的partition函数可以一次将数组的分割成以基准元素为分界点的序列。这么一想,这道题可以做成O(n)复杂度了。实现它需要的数据结构为双端链表。

//双端链表分割
	public void DivideLink(DoubleLink dl,int element){
		DoubleNode tail=dl.getEnd().getPre();
		DoubleNode first=dl.getFirst();
		first.setData(element);
		int start=1;
		int end=dl.getLength();
		int x=element;
		while(start<end){
			while(start<end && tail.getData()>=x)
			{
				end--;
				tail=tail.getPre();
			}
			if(start<end){
				start++;
				first.setData(tail.getData());
				first=first.getNext();
			}	
			while(start<end && first.getData()<=x)
			{
				start++;
				first=first.getNext();
			}
			if(start<end){
				tail.setData(first.getData());
				end--;
				tail=tail.getPre();

			}
		}
		first.setData(x);
	}

  循环链表的实现:

class DoubleLink{
	DoubleNode first;
	DoubleNode end;
	DoubleNode current;
	int length=0;
	//初始化空双端链表
	public DoubleLink(){
		first=new DoubleNode();
		end=new DoubleNode();
		first.setNext(end);
		end.setNext(first);
		first.setPre(end);
		end.setPre(first);
		current=first;
	}
	public int getLength(){
		return length;
	}
	public DoubleNode getFirst() {
		return first;
	}
	public void setFirst(DoubleNode first) {
		this.first = first;
	}
	public DoubleNode getEnd() {
		return end;
	}
	public void setEnd(DoubleNode end) {
		this.end = end;
	}
	public void InsertNode(DoubleNode node){
		current.setNext(node);
		node.setPre(current);
		current=node;
		end.setPre(node);
		node.setNext(end);
		length++;
	}
	public void RemoveNode(DoubleNode toDel){
		DoubleNode p = toDel.getPre();
		DoubleNode q = toDel.getNext();
		p.setNext(q);
		q.setPre(p);
		toDel.setPre(null);
		toDel.setNext(null);
//		if (toDel != first && toDel != end) {
//			p.setNext(q);
//			q.setPre(p);
//		}else if(toDel == first){
//			first=q;
//			p.setNext(q);
//			q.setPre(p);		
//		}else if(toDel == end){
//			end=p;
//			p.setNext(q);
//			q.setPre(p);
//		}
		length--;
	}
	public void UpdateNode(DoubleNode toUpdate,int data){
		toUpdate.setData(data);
	}
	public DoubleNode SearchNode(int data){
		DoubleNode p=first.getNext();
		while(p.getData()!=data && p!=end)
			p=p.getNext();
		if(p==end)
			p=null;
		return p;
	}
	public int IndexOfNode(DoubleNode search){
		int index=0;
		DoubleNode p=first;			
		while(p!=end && p!=search){
			p=p.getNext();
			index++;
		}
		if(p==end)
			index=-1;
		return index;	
	}
	public void printLink(){
		DoubleNode p=first.getNext();
		while(true){
			if(p==end)
				break;
			System.out.print(p.getData() + " ");
			p = p.getNext();

		}
		System.out.println();
	}
}
class DoubleNode{
	private int data;
	private DoubleNode pre;
	private DoubleNode next;
	public DoubleNode(){
		
	}
	public DoubleNode(int data){
		this.data=data;
	}
	
	public int getData() {
		return data;
	}
	public void setData(int data) {
		this.data = data;
	}

	public DoubleNode getPre() {
		return pre;
	}
	public void setPre(DoubleNode pre) {
		this.pre = pre;
	}
	public DoubleNode getNext() {
		return next;
	}
	public void setNext(DoubleNode next) {
		this.next = next;
	}
	
}

  

 

  • 问题四:链表加法,每一个节点为一个数位,分别分两种方法来实现,高位到低位、低位到高位。
//链表加法
	public LinkedList ListAdd1(LinkedList list1,LinkedList list2){
		Node current1=list1.getfirst();
		Node current2=list2.getfirst();
		LinkedList result=new LinkedList();
		int carry=0;
		int count;
		result.setfirst(new Node(-1));
		while(current1 != null && current2 != null){
			
			count=carry+current1.getData()+current2.getData();
			carry=count/10;
			count=count%10;
			Node reNode=new Node(count);
			result.addNode(reNode);
			current1=current1.getNext();
			current2=current2.getNext();
		}
		while(current1 != null){
			Node reNode=new Node(current1.getData()+carry);
			result.addNode(reNode);
			current1=current1.getNext();
			carry=0;
		}
		while(current2 != null){
			Node reNode=new Node(current2.getData()+carry);
			result.addNode(reNode);
			current1=current2.getNext();
			carry=0;
		}
		if(carry!=0){
			Node reNode=new Node(carry);
			result.addNode(reNode);
		}
		return result;
	}
	
	public LinkedList ListAdd2(LinkedList list1, LinkedList list2) {
		int number1 = 0;
		int number2 = 0;
		Node f1 = list1.getfirst();
		Node f2 = list2.getfirst();
		LinkedList result = new LinkedList();
		while (f1 != null) {
			number1 = number1 * 10 + f1.getData();
			f1 = f1.getNext();
		}
		while (f2 != null) {
			number2 = number2 * 10 + f2.getData();
			f2 = f2.getNext();
		}
		int count = number1 + number2;
		int rank=1;
		int temp=count;
		while(temp>10)
		{
			temp=temp/10;
			rank=rank*10;
		}
		temp=count;
		while (rank != 0) {
			result.addNode(new Node(temp/rank));
			temp%=rank;
			rank/=10;
		}

		return result;
	}

 

  • 问题五:链表查环,从哪个节点开始成环的,输出节点。

    这个解法还是hashmap。

//链表查环
	public Node findCircle(LinkedList list){
		HashMap<Node,Boolean> map=new HashMap<Node,Boolean>();
		Node p=list.getfirst();
		while(true){
			if(map.get(p)==null)
				map.put(p, true);
			else
				break;
			System.out.println(p.getData());
			p=p.getNext();
		}
		return p;
	}
  • 问题六:链表回文检测。每个数据位存储字符,一个字符串是否回文。

    关键点是使用栈来保存状态,到中点的时候开始pop,如果发现不是回文,则返回false。

//回文检测
	public <T> boolean LinkedPalindrome(ArrayList<T> list){
		boolean isPalindrome=true;
		ArrayList<T> stack=new ArrayList<T>();
		int i=-1;
		int isEven=list.size()%2;
		int mid=list.size()/2;
		System.out.println("mid="+mid);
		for(int j=0;j<list.size();j++){
			T data=list.get(j);
			if(j<mid)
			{
				stack.add(data);
				i++;
			}
			else {
				if (isEven == 0) {
					if(data==stack.get(i))
						{
							i--;
							continue;
						}else
						{
							isPalindrome=false;
							break;
						}
				}else if(isEven == 1){
					if(j==mid)
						continue;
					else{
						if(data==stack.get(i))
						{
							i--;
							continue;
						}else{
							isPalindrome=false;
							break;
						}
						
					}
						
				}
			}
			System.out.println("j="+data+" i="+stack.get(i));
		}
		
		return isPalindrome;
	}
  • 问题七:链表找环

    把我以前的代码复制过来了,追赶法。

bool IfCircle(LinkList *root)
{
    /*
        追赶式验证是否存在环
    */
    LinkList *fast=root;
    LinkList *slow=root;
    while(fast && slow)
    {
        fast=fast->p_next;
        fast=fast->p_next;
        if(fast==slow)
            return true;
        slow=slow->p_next;
    
    }
    return false;



}

 

posted @ 2016-07-31 18:18  天目山电鳗  阅读(228)  评论(0编辑  收藏  举报