算法与数据结构-链表

链表

底层存储结构
数组需要一块连续的内存空间来存储,对内存的要求比较高。如果我们申请一个 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);
	}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值