动态链表1线性表的链式存储结构(LinkedList实现)

  1.线性表的链式图。

 

  2.头结点和头指针的异同。

      头指针head

  •     头指针是指链表指向第一 个结点的指针,若链表有头结点,则是指向头结点的指针。
  •     头指针具有标识作用,所以常用头指针冠以链表的名字。
  •     无论链表是否为空,头指针均不为空。头指针是链表的必要元素。

      尾指针rear

  •     同头指针一样,但是指向链表中最后一个节点,最后一个节点指向的是一个null。

      头结点

  •      虚拟头结点:为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域般无意义(也可存放链表的长度)有了                              头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了。
  •      真实头结点:第一个节点,也就是存储元素的一个节点。

  3.数据域和指针域

 

   4.线性表的链式存储结构LinkedList的定义。

      

    分析:

    1、单链表的插入----插入元素e。

    头插法:1.当该链表是一个空链表时:

                       将头结点的下一跳赋给插入元素e的下一跳,然后将该元素e赋给头结点的下一跳。最后将尾指针rear移动到元素e对                        应的节点。

                 2.当该链表是一个存在元素的链表时:

                       将头结点的下一跳赋给插入元素e的下一跳,然后将该元素e赋给头结点的下一跳。这里的尾指针rear则不需要移                              动。

     尾插法:将该元素e赋给尾指针对应的结点的下一跳,然后将尾指针rear移动到元素e对应的结点。

     一般插入(中间某个位置插入):比如说在A和B中间插入一个元素。

                     将A的下一跳赋给元素e的下一跳,然后将元素e赋给A的下一跳,由于中间插入,尾指针rear不需要移动。

    思路算法 :

                         1. 声明一个节点p指向链表虚拟头节点,初始化i从0开始。

                         2. 当i<index时,就遍历链表,让p的指针向后移动,不断指向下一个节点,i累加1。

                         3. 若查到末尾p为空,则说明第index个元素不存在,抛异常throw new IllegalArgumentException("插入角标非                                      法!")。

                         4. 否则查找成功,在系统生成一个空节点n。

                         5. 将插入元素e赋值给n.data。

                         6. 单链表的插入标准语句n.next=p.next;  p.next=n;

public void add(int index, E e) {
		if(index<0||index>size){
			throw new IllegalArgumentException("插入角标非法!");
		}
		Node n=new Node(e,null);
		if(index==0){	               //头插
			n.next=head.next;
			head.next=n;
			if(size==0){
				rear=n;
			}
		}else if(index==size){	       //尾插
			rear.next=n;
			rear=rear.next;
		}else{                         //一般插入
			Node p=head;
			for(int i=0;i<index;i++){
				p=p.next;
			}
			n.next=p.next;
			p.next=n;
		}
		size++;
		
	}

     2、单链表的删除----删除index处的元素

    删头:1.当该链表只存在一个元素时:

                   将真实头结点的下一跳赋给虚拟头结点的下一跳。这里的尾指针rear移动到虚拟头结点处。

               2.当该链表不止一个元素的时候:

                   将真实头结点的下一跳赋给虚拟头结点的下一跳。这里的尾指针rear不需要移动。

    删尾:将尾结点的下一跳赋给尾结点上一跳的下一跳,这里的尾指针rear移动到上一个位置处。

    一般删除(删除中间某个位置index处元素):       

                 将index处的下一跳赋给index处上一跳的下一跳,这里的尾指针rear不需要移动。

    思路算法:

             1. 声明一个节点p指向链表的虚拟头结点,初始化i从0开始。

             2. 当i<index时,就遍历链表,让p的指针向后移动,不断指向下一个结点,i累加1。

             3. 若到链表末尾p为空,则说明第index个元素不存在,抛异常 。                                                                                              

             4. 否则查找成功,将欲删除的结点p.next赋值给del。                                                                                                                    

             5. 单链表的删除标准语句res=del.data; p.next=del.next;   del.next=null;   del=null;                                                                  

             6. 将del.data赋值给res,也就是要删除的元素,作为返回。                                                                                                        

             7.  释放res值。

public E remove(int index) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("删除角标非法!");
		}
		E res=null;                        //定义一个返回值,初始值为空
		if(index==0){	                      //删头
			Node p=head.next;
			res=p.data;
			head.next=p.next;
			p.next=null;
			p=null;
			if(size==1){
				rear=head;
			}
		}else if(index==size-1){              //删尾
			Node p=head;
			res=rear.data;
			while(p.next!=rear){
				p=p.next;
			}
			p.next=null;
			rear=p;
		}else{                               一般删除
			Node p=head;
			for(int i=0;i<index;i++){
				p=p.next;
			}
			Node del=p.next;       //将下一跳也就是删除元素赋给del
			res=del.data;          //将要删除的元素具体值赋给res
			p.next=del.next;       //将要删除元素的下一跳给p的下一跳
			del.next=null;         //删除元素的下一跳清空
			del=null;              //删除元素清空
		}
		size--;
		return res;
	}

    3、单链表的获取----index处元素。

      算法思路:

      1. 对index进行判断,若index<或者index>=size,抛异常throw new IllegalArgumentException("查找角标非法!");。

      2. 如果index==0,直接返回头结点下一跳元素,也就是return head.next.data;。

      3. 如果index==size-1,直接返回尾指针对应的元素,也就是return rear.data;。

      4.如果index在链表中间的某元素,创建一个Node类型的p变量,将头结点的地址给p。然后定义一个for循环,将p的地址移动          到目标index处的结点。最后返回return p.data;。

public E get(int index) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("查找角标非法!");
		}
		if(index==0){
			return head.next.data;
		}else if(index==size-1){
			return rear.data;
		}else{
			Node p=head;
			for(int i=0;i<=index;i++){
				p=p.next;
			}
			return p.data;
		}
	}

    4、单链表的修改----(index,e)

      算法思路:

      1.  对index进行判断,若index<或者index>=size,抛异常throw new IllegalArgumentException("修改角标非法!");。

      2. 若index==0,直接令头结点的下一跳的数据data等于e。

      3. 如果index==size-1,直接令尾指针对应的元素等于e,也就是rear.data=e;。

      4. 如果角标在链表中间某位置,创建一个Node类型的变量p,等于head的地址。

      5. 建立一个for循环,从0~idnex,令p到达修改元素位置,最后p.data=e;。

public void set(int index, E e) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("修改角标非法!");
		}
		if(index==0){
			head.next.data=e;
		}else if(index==size-1){
			rear.data=e;
		}else{
			Node p=head;
			for(int i=0;i<=index;i++){
				p=p.next;
			}
			p.data=e;
		}
	}

    5、单链表的查找----e

      1. 令未查找到元素返回初值为int index=-1;

      2. 判断链表是否为空,空返回定义的初值return index;

      3. 创建一个Node类型的p变量,让头指针的地址指向p。

      4. 开始一个循环,让p从头结点走向尾结点,每走一步index++,然后与e判断是否相等,相等返回index当前值,不等则返回定义的初值。

public int find(E e) {
		int index=-1;
		if(isEmpty()){
			return index;
		}
		Node p=head;
		while(p.next!=null){
			p=p.next;
			index++;
			if(p.data==e){
				return index;
			}
		}
		return -1;
	}

    6、toString方法的创建。

      1. 使用StringBuilder创建一个对象sb。

      2. 对单链表进行判空,空则打印"[ ]"   

      3. 非空,先打印"[ "  当创建一个Node类型的p变量,将头结点的地址赋给p,开启一个0~rear的循环,每次都将新的地址对应            的元素打印出来,然后元素后边加上","  除非打印到最后一个元素后边才加上"] "

      4. 最后返回sb.toString

public String toString() {
		StringBuilder sb=new StringBuilder();
		sb.append("LinkedList:size="+getSize()+"\n");
		if(isEmpty()){
			sb.append("[]");
		}else{
			sb.append("[");
			Node p=head;
			while(p.next!=null){
				p=p.next;
				if(p==rear){
					sb.append(p.data+"]");
				}else{
					sb.append(p.data+",");
				}
			}
			
		}
		return sb.toString();
	}

  代码的全部实现:

package com.oupeng.p5链表;

import com.oupeng.p1线性表.List;

public class LinkedList<E> implements List<E> {
	/**
	 * 单向链表的结点类
	 * */
	private class Node{
		E data;		//数据域
		Node next;	//指针域
		public Node(){
			this(null,null);
		}
		public Node(E data,Node next){
			this.data=data;
			this.next=next;
		}
		@Override
		public String toString() {
			return data.toString();
		}
	}
	
	private Node head;	//指向虚拟头结点的头指针
	private Node rear;	//指向尾结点的尾指针
	private int size;	//记录元素的个数
	
	public LinkedList(){
		head=new Node();
		rear=head;
		size=0;
	}
	public LinkedList(E[] arr){
		head=new Node();
		rear=head;
		size=0;
	}
	
	@Override
	public int getSize() {
		return size;
	}

	@Override
	public boolean isEmpty() {
		return size==0&&head.next==null;
	}

	@Override
	public void add(int index, E e) {
		if(index<0||index>size){
			throw new IllegalArgumentException("插入角标非法!");
		}
		Node n=new Node(e,null);
		if(index==0){	//头插
			n.next=head.next;
			head.next=n;
			if(size==0){
				rear=n;
			}
		}else if(index==size){	//尾插
			rear.next=n;
			rear=rear.next;
		}else{
			Node p=head;
			for(int i=0;i<index;i++){
				p=p.next;
			}
			n.next=p.next;
			p.next=n;
		}
		size++;
		
	}

	@Override
	public void addFirst(E e) {
		add(0,e);
	}

	@Override
	public void addLast(E e) {
		add(size,e);
	}

	@Override
	public E get(int index) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("查找角标非法!");
		}
		if(index==0){
			return head.next.data;
		}else if(index==size-1){
			return rear.data;
		}else{
			Node p=head;
			for(int i=0;i<=index;i++){
				p=p.next;
			}
			return p.data;
		}
	}

	@Override
	public E getFirst() {
		return get(0);
	}

	@Override
	public E getLast() {
		return get(size-1);
	}

	@Override
	public void set(int index, E e) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("修改角标非法!");
		}
		if(index==0){
			head.next.data=e;
		}else if(index==size-1){
			rear.data=e;
		}else{
			Node p=head;
			for(int i=0;i<=index;i++){
				p=p.next;
			}
			p.data=e;
		}
	}

	@Override
	public boolean contains(E e) {
		
		return find(e)!=-1;
	}

	@Override
	public int find(E e) {
		int index=-1;
		if(isEmpty()){
			return index;
		}
		Node p=head;
		while(p.next!=null){
			p=p.next;
			index++;
			if(p.data==e){
				return index;
			}
		}
		return -1;
	}

	@Override
	public E remove(int index) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("删除角标非法!");
		}
		E res=null;
		if(index==0){	//头删
			Node p=head.next;
			res=p.data;
			head.next=p.next;
			p.next=null;
			p=null;
			if(size==1){
				rear=head;
			}
		}else if(index==size-1){//尾删
			Node p=head;
			res=rear.data;
			while(p.next!=rear){
				p=p.next;
			}
			p.next=null;
			rear=p;
		}else{
			Node p=head;
			for(int i=0;i<index;i++){
				p=p.next;
			}
			Node del=p.next;
			res=del.data;
			p.next=del.next;
			del.next=null;
			del=null;
		}
		size--;
		return res;
	}

	@Override
	public E removeFirst() {
		return remove(0);
	}

	@Override
	public E removeLast() {
		return remove(size-1);
	}

	@Override
	public void removeElement(E e) {
		int index=find(e);
		if(index==-1){
			throw new IllegalArgumentException("元素不存在");
		}
		remove(index);
	}

	@Override
	public void clear() {
		head.next=null;
		rear=head;
		size=0;
	}
	
	@Override
	public String toString() {
		StringBuilder sb=new StringBuilder();
		sb.append("LinkedList:size="+getSize()+"\n");
		if(isEmpty()){
			sb.append("[]");
		}else{
			sb.append("[");
			Node p=head;
			while(p.next!=null){
				p=p.next;
				if(p==rear){
					sb.append(p.data+"]");
				}else{
					sb.append(p.data+",");
				}
			}
			
		}
		return sb.toString();
	}
     public boolean equals(Object obj){
		if(obj==null){
			return false;
		}
		if(obj==this){
			return true;
		}
		if(obj instanceof LinkedList){
			LinkedList<E> l=(LinkedList<E>) obj;
			if(l.getSize()==getSize()){
				for(int i=0;i<getSize();i++){
					if(get(i)!=l.get(i)){
						return false;
					}
				}
				return true;
			}
		}
		return false;
	}
}

    5.单链表结构与顺序存储结构的优缺点。

      存储分配方式:

  •     顺序存储结构用一段连续的存储单元依次存储线性表的数据元素。
  •     单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素。

      时间性能:

  •     查找   顺序存储结构O(1)   单链表O(n)
  •     插入和删除   顺序存储结构需要平均移动表长一半的元素,时间为O(n)
  •                            单链表在线出某位置的指针后,插入和删除时间仅为O(1)

      空间性能:

  •     顺序存储结构需要预分配存储空间,分大了,浪费,分小了容易发生上溢。
  •     单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值