2.获取第一个元素和最后一个元素
//获取第一个元素 public T getFirst() { //需要判断链表是否为空 if (isEmpty()) { return null; } return head.next.item; } //获取最后一个元素 public T getLast() { if (isEmpty()) { return null; } return last.item; }
解析:无论是获取哪个,我们都需要获取判空操作,养成好习惯,代码一定要考虑进所有情况,否则容易出异常。因为是获取元素,所以我们返回的一定是某个结点的item,第一个元素,说明就是头结点的next的item,最后一个元素就是返回last的item就好。
3.添加元素t
//插入元素t public void add(T t) { //如果链表为空 if (isEmpty()) { //创建新结点 Node newNode = new Node(t, head, null); //让新节点为尾结点 last = newNode; //让头结点指向尾结点 head.next = last; } else { //如果链表不为空 Node oldLast = last; //创建新节点 Node newNode = new Node(t, oldLast, null); //让当前的尾结点指向新结点 oldLast.next = newNode; //让新节点成为尾结点 last = newNode; } N++; }
解析:同样我们需要对链表进行判空操作,如果链表为空,我们需要new一个新节点存入数据t,因为就它一个结点,所以我们把它赋值给last,让它成为尾结点,然后让head指向它即可。如果链表不为空,我们首先需要用一个结点oldLast记录旧的尾结点,同时new一个新节点放入数据t,让它成为新的尾结点last,同时让旧的尾结点oldLast指向它。最后无论怎样都记得让N++。
4.向指定位置i插入元素t
//向指定位置i位置插入元素t public void insert(int i,T t){ //找到i位置的前一个节点 Node a=head; for (int j = 0; j < i; j++) { a=a.next; } //找到i位置的节点 Node curr = a.next; //创建新节点 Node newNode = new Node(t, a, curr); //让i位置的前一个节点的下一个节点变为新节点 a.next=newNode; //让i位置的前一个节点变为新节点 curr.pre=newNode; //元素个数加1 N++; }
具体的步骤通过下图就能明白,一定要搞清楚每一步的顺序,否则很容易出错,自己可以草稿画图模拟。
5.获取指定位置i处的元素
//获取指定位置i处的元素 public T get(int i){ Node a=head; for (int j = 0; j <=i; j++) { a=a.next; } return a.item; }
解析:和单链表相同,从头结点开始遍历打i处,返回item即可。
6.找到元素t第一次出现的位置
//找到元素t第一次出现的位置 public int indexOf(T t){ Node a=head; for (int i = 0; i < N; i++) { a=a.next; if(a.item==t) return i; } return -1; }
解析:单单链表一致,从头结点开始遍历,同时判断是否是查找的元素,如果找到返回位置i,否则循环结束后返回-1。
7.删除位置i的元素,并返回该元素
//删除位置i处的元素,并返回该元素 public T remove(int i){ Node a=head; //找到i节点 for (int j = 0; j <= i; j++) { a=a.next; } a.next.pre=a.pre; a.pre.next=a.next; N--; return a.item; }
如下图解即可明白,主要是要分清步骤,不要搞混,返回的是值不是结点。
总结:双向链表与单链表虽然有所区别,但方法的实现大同小异,我们也应该熟练掌握,多画图更易帮助我们理解,都是基础的知识。