• 链表问题总结


    • 链表

      链表是一种基本的一维离散存储数据结构。相对于数组,它是离散内存的,不能如数组一样通过下标来查询相应地数据,每一个链表节点只能知道它的上一个(双端链表)和它的下一个(单链表,双端链表)节点。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;
    
    
    
    }
  • 相关阅读:
    机器学习之--画图补充
    机器学习之--KNN算法简单实现
    redhat centos yum源的安装
    redhat6.5 linux 安装mysql5.6.27
    bash 截取字符串
    redhat vim编辑器永久添加行号及搜索
    Orthomcl的详细使用
    InterProScan 5.25-64.0 安装和使用
    paml正选择处理时序列里有终止密码子怎么处理掉
    R语言putty中直接使用X11(Xming)绘图
  • 原文地址:https://www.cnblogs.com/chentingk/p/5723592.html
Copyright © 2020-2023  润新知