2、查找-插值查找

    线性表实现之二:链表

 

    思路:链表采用链式存储结构,在内部只需要将一个一个结点链接起来(每个结点中有关于此结点下一个结点的引用)即可。

    优点:因为每个结点记录下个结点的引用,则在进行插入和删除操作时,只需要改变对应下标下结点的引用即可。

    缺点:要得到某个下标的数据,不能通过下标直接得到,需要遍历整个链表。

 

/**
 * 自己用链式存储实现的线性表
 */
public class LinkedList<E> {

	private Node<E> header = null;// 头结点
	int size = 0;// 表示数组大小的指标

	public LinkedList() {
		this.header = new Node<E>();
	}

	public boolean add(E e) {
		if (size == 0) {
			header.e = e;
		} else {
			// 根据需要添加的内容,封装为结点
			Node<E> newNode = new Node<E>(e);
			// 得到当前最后一个结点
			Node<E> last = getNode(size-1);
			// 在最后一个结点后加上新结点
			last.addNext(newNode);
		}
		size++;// 当前大小自增加1
		return true;
	}

	public boolean insert(int index, E e) {
		Node<E> newNode = new Node<E>(e);
		// 得到第N个结点
		Node<E> cNode = getNode(index);
		newNode.next = cNode.next;
		cNode.next = newNode;
		size++;
		return true;

	}

	/**
	 * 遍历当前链表,取得当前索引对应的元素
	 * 
	 * @return
	 */
	private Node<E> getNode(int index) {
		// 先判断索引正确性
		if (index > size || index < 0) {
			throw new RuntimeException("索引值有错:" + index);
		}
		Node<E> tem = new Node<E>();
		tem = header;
		int count = 0;
		while (count != index) {
			tem = tem.next;
			count++;
		}
		return tem;
	}

	/**
	 * 根据索引,取得该索引下的数据
	 * 
	 * @param index
	 * @return
	 */
	public E get(int index) {
		// 先判断索引正确性
		if (index >= size || index < 0) {
			throw new RuntimeException("索引值有错:" + index);
		}
		Node<E> tem = new Node<E>();
		tem = header;
		int count = 0;
		while (count != index) {
			tem = tem.next;
			count++;
		}
		E e = tem.e;
		return e;
	}

	public int size() {
		return size;
	}

	/**
	 * 设置第N个结点的值
	 * 
	 * @param x
	 * @param e
	 * @return
	 */
	public boolean set(int index, E e) {
		// 先判断索引正确性
		if (index > size || index < 0) {
			throw new RuntimeException("索引值有错:" + index);
		}
		Node<E> newNode = new Node<E>(e);
		// 得到第x个结点
		Node<E> cNode = getNode(index);
		cNode.e = e;
		return true;
	}

	/**
	 * 用来存放数据的结点型内部类
	 */
	class Node<e> {
		private E e;// 结点中存放的数据

		Node() {
		}

		Node(E e) {
			this.e = e;
		}

		Node<E> next;// 用来指向该结点的下一个结点

		// 在此结点后加一个结点
		void addNext(Node<E> node) {
			next = node;
		}
	}

}

 

    

    问题关于add()方法—我们每次添加一个数据时,都需要遍历整个表,得到表尾,再在表尾添加,这是很不科学的。

    改进设立一个Node<E>类的成员变量end来指示表尾,这样每次添加时,就不需要再重新遍历得到表尾。

    改进后add()方法如下:

 

	public boolean add(E e) {
		if (size == 0) {
			header.e = e;
		} else {
			// 根据需要添加的内容,封装为结点
			Node<E> newNode = new Node<E>(e);
			//在表尾添加元素
			last.addNext(newNode);
                                         //将表尾指向当前最后一个元素
			last = newNode;
		}
		size++;// 当前大小自增加1
		return true;
	}

 

 

    PS在Java语言中,通过链表实现的线性表,对应着Collection接口中的子接口List接口,其数组实现可参考类LinkedList源码实现的是双向链表)。

 

/**
 * Java 实现的双向链表。 
 * 注:java自带的集合包中有实现双向链表,路径是:java.util.LinkedList
 */
public class DoubleLink<T> {

    // 表头
    private DNode<T> mHead;
    // 节点个数
    private int mCount;

    // 双向链表“节点”对应的结构体
    private class DNode<T> {
        public DNode prev;
        public DNode next;
        public T value;

        public DNode(T value, DNode prev, DNode next) {
            this.value = value;
            this.prev = prev;
            this.next = next;
        }
    }

    // 构造函数
    public DoubleLink() {
        // 创建“表头”。注意:表头没有存储数据!
        mHead = new DNode<T>(null, null, null);
        mHead.prev = mHead.next = mHead;
        // 初始化“节点个数”为0
        mCount = 0;
    }

    // 返回节点数目
    public int size() {
        return mCount;
    }

    // 返回链表是否为空
    public boolean isEmpty() {
        return mCount==0;
    }

    // 获取第index位置的节点
    private DNode<T> getNode(int index) {
        if (index<0 || index>=mCount)
            throw new IndexOutOfBoundsException();

        // 正向查找
        if (index <= mCount/2) {
            DNode<T> node = mHead.next;
            for (int i=0; i<index; i++)
                node = node.next;

            return node;
        }

        // 反向查找
        DNode<T> rnode = mHead.prev;
        int rindex = mCount - index -1;
        for (int j=0; j<rindex; j++)
            rnode = rnode.prev;

        return rnode;
    }

    // 获取第index位置的节点的值
    public T get(int index) {
        return getNode(index).value;
    }

    // 获取第1个节点的值
    public T getFirst() {
        return getNode(0).value;
    }

    // 获取最后一个节点的值
    public T getLast() {
        return getNode(mCount-1).value;
    }

    // 将节点插入到第index位置之前
    public void insert(int index, T t) {
        if (index==0) {
            DNode<T> node = new DNode<T>(t, mHead, mHead.next);
            mHead.next.prev = node;
            mHead.next = node;
            mCount++;
            return ;
        }

        DNode<T> inode = getNode(index);
        DNode<T> tnode = new DNode<T>(t, inode.prev, inode);
        inode.prev.next = tnode;
        inode.next = tnode;
        mCount++;
        return ;
    }

    // 将节点插入第一个节点处。
    public void insertFirst(T t) {
        insert(0, t);
    }

    // 将节点追加到链表的末尾
    public void appendLast(T t) {
        DNode<T> node = new DNode<T>(t, mHead.prev, mHead);
        mHead.prev.next = node;
        mHead.prev = node;
        mCount++;
    }

    // 删除index位置的节点
    public void del(int index) {
        DNode<T> inode = getNode(index);
        inode.prev.next = inode.next;
        inode.next.prev = inode.prev;
        inode = null;
        mCount--;
    }

    // 删除第一个节点
    public void deleteFirst() {
        del(0);
    }

    // 删除最后一个节点
    public void deleteLast() {
        del(mCount-1);
    }
}

 

 

    对比顺序表和链式表
     前面已经说过顺序表和链式表各自的特点,这里在重申一遍

     数组操作优点:1.通过索引快速定位到下标,查询快速。

             缺点:1.数组声明时即需要确定数组大小。当操作中超过容量时,则需要重新声明数组,并且复制当前所有数据;

                   2.当需要在中间进行插入或者删除时,则需要移动大量元素(size-index个)。

          链表操作优点:1.因为每个结点记录下个结点的引用,则在进行插入和删除操作时,只需要改变对应下标下结点的引用即可。

             缺点:1.要得到某个下标的数据,不能通过下标直接得到,需要遍历整个链表。

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值