链表

底层存储结构
数组需要一块连续的内存空间来存储,对内存的要求比较高。如果我们申请一个 100MB 大小的数组,当内存中没有连续
的、足够大的存储空间时,即便内存的剩余总可用空间大于 100MB,仍然会申请失败。而链表恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用。
单链表
我们把内存块称为链表的“结点”。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要
记录链上的下一个结点的地址。如图所示,我们把这个记录下个结点地址的指针叫作后继指针 next。
算法与数据结构-链表
特点
我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是
链表上最后一个结点。
插入、删除
算法与数据结构-链表
循环链表
双向链表,顾名思义,它支持两个方向,每个结点不止有一个后继指针 next 指向后面的结点,还有一
个前驱指针 prev 指向前面的结点。
算法与数据结构-链表
双向链表
算法与数据结构-链表
写好链表得技巧

  1. 理解指针或引用的含义
    将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针 中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

  2. 警惕指针丢失和内存泄漏

  3. 利用哨兵简化实现难度
    如果我们引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个 哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫 作不带头链表。
    哨兵结点是不存储数据的。因为哨兵结点一直存在, 所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为 相同的代码实现逻辑了。

算法与数据结构-链表

  1. 重点留意边界条件处理
  2. 举例画图,辅助思考
    单链表得demo
package com.wanda.list;
/**
 * 单链表的实现
 * @author tomcat
 *
 * @param <E>
 */
public class LinkedList<E>{
	private class Node{
		public E e;//数据域
		public Node next;//指向下一个节点的指针
		public Node(E e,Node next) {
			this.e=e;
			this.next=next;
		}
		public Node(E e) {
			this(e,null);
		}
		public Node() {
			this(null,null);
		}
		@Override
		public String toString() {
			return e.toString();
		}
	}
	//虚拟头节点,不存储任何数据
	private Node dummyHead;
	int size;
	public LinkedList() {
		dummyHead=new Node(null,null);
		size=0;
	}
	public int getSize(){
		return size;
		
	}
	public boolean isEmpty() {
		return size==0;
	}
	public void addFirst(E value) {
		/*Node node=new Node(value);
		node.next=head;
		head=node;*/
		add(0,value);
		size++;
	}
	//添加元素
	public void add(int index,E value) {
		if(index<0||index>size) {
			throw new IllegalArgumentException("add failed");
		}
	
			Node prev=dummyHead;
			for(int i=0;i<index;i++) {
				prev=prev.next;
			}
			Node node=new Node(value);
			node.next=prev.next;
			prev.next=node;
			//prev.next=new Node(value,prev.next);
			size++;
		}

	public void addLast(E value) {
		add(size,value);
	} 
	public E get(int index) {
		if(index<0||index>size)
			throw new IllegalArgumentException("get failed");
		Node cur=dummyHead.next;
		for(int i=0;i<index;i++) {
			cur=cur.next;
		}
		return cur.e;
	}
	public E getFirst() {
		return get(0);
	}
	public E getLast() {
		return get(size-1);
	}
	public void set(int index,E e) {
		if(index<0||index>=size) {
			throw new IllegalArgumentException("set failed");
		}
		Node cur=dummyHead.next;
		for(int i=0;i<index;i++) {
			cur=cur.next;
		}
		cur.e=e;
	}
	public boolean contains(E e) {
		Node cur=dummyHead.next;
		while(cur!=null) {
			if(cur.e.equals(e)) {
				return true;
			}
			cur=cur.next;
		}
		return false;
	}
	public E remove(int index) {
		if(index<0||index>=size) {
			throw new IllegalArgumentException("remove failed");
		}
		Node prev=dummyHead.next;
		for(int i=0;i<index-1;i++) {
			prev=prev.next;
		}
		Node retNode=prev.next;
		prev.next=retNode.next;
		retNode.next=null;
		size--;
		return retNode.e;
	}
	public E removeFirst() {
		return remove(0);
	}
	public E removeLast() {
		return remove(size-1);
	}
	public void removeElement(E e) {
		Node prev=dummyHead;
		while(prev.next!=null) {
			if(prev.next.e.equals(e))
				break;
			prev=prev.next;
		}
		if(prev.next!=null) {
			Node delNode=prev.next;
			prev.next=delNode.next;
			delNode.next=null;
		}
	}
	@Override
	public String toString() {
		StringBuilder res=new StringBuilder();
		Node cur=dummyHead.next;
		while(cur!=null) {
			res.append(cur+"->");
			cur=cur.next;	
		}
		res.append("Null");
		return res.toString();
			
	}
}

基于链表得队列

package com.wanda.list;

import com.wanda.interfaces.Queue;


public class LinkedListQueue<E> implements Queue<E> {
	private class Node{
		public E e;
		public Node next;
		public Node(E e,Node next) {
			this.e=e;
			this.next=next;
		}
		public Node(E e) {
			this(e,null);
		}
		public Node() {
			this(null,null);
		}
		@Override
		public String toString() {
			return e.toString();
		}
	}
	private Node head,tail;
	private int size;
	public LinkedListQueue() {
		// TODO Auto-generated constructor stub
		head=null;
		tail=null;
		size=0;
	}
	

	@Override
	public void enqueue(E value) {
		// TODO Auto-generated method stub
		if(tail==null) {
			tail=new Node(value);
			head=tail;
			}else {
				tail.next=new Node(value);
				tail=tail.next;
			}
		size++;
		
	}

	@Override
	public E dequeue() {
		// TODO Auto-generated method stub
		if(isEmpty()) {
			throw new IllegalArgumentException("can not dequeue from an empty queue");
		}
		Node retNode=head;
		head=head.next;
		retNode.next=null;
		if(head==null) {
			tail=null;
		}
		size--;
		return retNode.e;
	}

	@Override
	public E getFront() {
		// TODO Auto-generated method stub
		if(isEmpty()) {
			throw new IllegalArgumentException("can not dequeue from an empty queue");
		}
		return head.e;
	}

	@Override
	public int getSize() {
		// TODO Auto-generated method stub
		return size;
	}

	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return size==0;
	}

	@Override
	public String toString() {
		StringBuilder res=new StringBuilder();
		res.append("Queue: front");
		Node cur=head;
		while(cur!=null) {
			res.append(cur+"->");
			cur=cur.next;
		}
		res.append("NULL tail");
		return res.toString();
	}

	public static void main(String[] args) {
		LinkedListQueue<Integer> queue=new LinkedListQueue<>();
		for(int i=0;i<10;i++) {
			queue.enqueue(i);
			System.out.println(queue);
		}
	}
}

链表的应用测试是否是回文数

package com.wanda.list;

import java.util.Scanner;
class Node{	//定义节点类
	  char data;
	  Node next;
	 public Node(char t){
		 this.data=t;
	 }	
	 
}
public class IsHuiWen {
	
	public static boolean IsHuiwen(Node head){
		Node n1=head;
		Node n2=head;
//		System.out.println(n2.next.next.data);
		while(n2.next!=null&&n2.next.next!=null){	
//这里有个很容易出错的地方是 如果while循环的循环条件没有加上n2.next!=null则会抱空指针异常,以为当n2已经是最后一个了 则不存在n2.next.next
			//快慢指针 n2前进2步 n1前进1步
			n1=n1.next;
			n2=n2.next.next;
		}//循环结束时n2指向最末尾节点,n1刚好指向中间节点
		
		n2=n1.next;
		Node pre=n1;
		Node next=null;
		while(n2!=null){
			//后半部分节点逆序指向
			next=n2.next;
			n2.next=pre;
			pre=n2;
			n2=next;
		}
		
		n1.next=null;
		n2=pre;
		n1=head;
		while(n1.next!=null&&n2.next!=null){
			//原单链表的前后节点 进行逐一比较 
			if(n1.data!=n2.data)
				return false;
			n1=n1.next;
			n2=n2.next;
		}
		return true;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in=new Scanner(System.in);
		String tmp=in.nextLine();
		char[] s=tmp.toCharArray();
		
		Node[] node=new Node[s.length];
		for(int i=0;i<s.length;i++){
			node[i]=new Node(s[i]);				
		}
		for(int i=0;i<s.length;i++){
			if(i==s.length-1){	//最后一个节点,指针指向空
				node[i].next=null;
			}
			else node[i].next=node[i+1];
		}
		boolean flag=IsHuiwen(node[0]);
		System.out.println("链表是回文吗? "+flag);
	}

}

相关文章: