练习1•:链式栈
//链式栈(top指向第一位无效的头结点) public class LinkedStack<E> { Node<E> top; //内部类 class Node<E> { protected E data; protected Node<E> next; public Node(E data) { this.data = data; } } public LinkedStack() { top = new Node(new Object()); } //头插法 public void push(E val){ Node<E> newNode=new Node(val) ;//创建一个值为val的节点 newNode.next=top.next; //新插入节点的next指向原top指向的next top.next=newNode;//再把top.next指向新节点 } //获取栈顶元素并删除 public E remove(){ if(top.next==null){ throw new UnsupportedOperationException("the stack has been empty"); } E result=top.next.data; top.next=top.next.next; return result; } public E peek(){ if(top.next==null){ throw new UnsupportedOperationException("the stack has been empty"); } return top.next.data; } //从top节点的下一个开始遍历,不为空则一直的打印 public void show(){ Node<E> tmp=top.next; while (tmp!=null){ System.out.print(tmp.data+" "); tmp=tmp.next; } System.out.println(); } public static void main(String[] args) { LinkedStack<Integer> l=new LinkedStack(); l.push(3); l.push(4); l.push(5); l.show(); System.out.println(l.peek()); l.remove();//删除5 l.show(); l.remove();//删除4 l.show(); l.remove();//删除3 l.show(); } }
练习2:查找链表中倒数第k个节点
//单链表查找倒数第k个节点 class FindLastK<E> { Node<E> head; class Node<E> { protected E data; protected Node<E> next; public Node(E data,Node<E> next) { this.data = data; this.next=next; } } // //构造函数,第一个头结点有效,所以不需要构造函数 // public FindLastK() { // this.head = new Node(new Object(),null); // } //找倒数第k个数的方法 public E lastK(int k){ Node<E> cur1=this.head.next; Node<E> cur2=this.head;//cur2指向头 if(head==null){ return null; } if(k>getLenth()||k<=0){ return null; } else if(k==getLenth()){ return head.data; } for(int i=1;i<=k;i++) { cur2=cur2.next; if(cur2==null){ return null; } } while (cur2.next!=null){ cur2=cur2.next; cur1=cur1.next; } return cur1.data; } //获取链表长度 public int getLenth(){ int length=0; Node<E> cu=head; if(head==null){ return 0; } while (cu!=null){ //应该让cur去遍历,不能让head直接遍历,否则打印一次后show再次打印链表就会空 length++; cu=cu.next; } return length; } //尾插法 public void add(E val) { Node<E> newNode = new Node(val,null); Node<E> current = head; if(head==null){ head=newNode; return; } while (current.next != null) { current = current.next; } current.next = newNode; // newNode.next=null; } public void show() { Node<E> current = head; if(current==null){ System.out.println("链表空!!"); return; } while (current!=null&¤t.next!= null) { System.out.print(current.data + " "); current = current.next; } System.out.println(current.data); } } public class FindLastKTest{ public static void main(String[] args) { FindLastK<Integer> f=new FindLastK<>(); f.add(3); f.add(4); f.add(5); f.add(6); f.show(); System.out.println("该链表的长度:"+f.getLenth()); System.out.println(f.lastK(1));//6 System.out.println(f.lastK(4));//3 System.out.println(f.lastK(5));//null f.show(); } }
练习3:找到带环链表的入口节点
import sun.awt.image.ImageWatched; //单链表查找倒数第k个节点 public class LinkedExercise<E> { Node<E> head; static class Node<E> { protected E data; public Node<E> next; public Node(E data, Node<E> next) { this.data = data; this.next = next; } } // //构造函数,第一个头结点有效,所以不需要构造函数 // public FindLastK() { // this.head = new Node(new Object(),null); // } //找倒数第k个数的方法 public E lastK(int k) { Node<E> cur1 = this.head.next; Node<E> cur2 = this.head;//cur2指向头 if (head == null) { return null; } else if (k > getLenth() || k <= 0) { return null; } //如果找的倒数第k个恰好为链表长度,直接将头结点的数返回 else if (k == getLenth()) { return head.data; } for (int i = 1; i <= k; i++) { cur2 = cur2.next; if (cur2 == null) { return null; } } //两个节点同时遍历,快节点遍历到最后一个节点时,慢节点指向的节点就是要找的节点 while (cur2.next != null) { cur2 = cur2.next; cur1 = cur1.next; } return cur1.data; } //获取链表长度 public int getLenth() { int length = 0; Node<E> cu = head; if (head == null) { return 0; } while (cu != null) { //应该让cur去遍历,不能让head直接遍历,否则打印一次后show再次打印链表就会空 length++; cu = cu.next; } return length; } //判断单链表是否有环 /** * 快慢指针,先通过两个指针找到环内的节点,然后再一个节点从相交节点出发, * 另一个节点从头结点出发,再次相交的节点就是环的入口节点 * * @return */ public E getLinkCirclrVal() { Node<E> slow = this.head; Node<E> fast = this.head; //找到了相交节点 while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { break; } } if (fast == null) { return null; } else { fast = this.head; while (fast != slow) { fast = fast.next; slow = slow.next; } return slow.data; } } //尾插法 public void add(E val) { Node<E> newNode = new Node(val, null); Node<E> current = head; if (head == null) { head = newNode; return; } while (current.next != null) { current = current.next; } current.next = newNode; // newNode.next=null; } public void show() { Node<E> current = head; if (current == null) { System.out.println("链表空!!"); return; } while (current != null && current.next != null) { System.out.print(current.data + " "); current = current.next; } System.out.println(current.data); } //构造带环的链表 public void con(LinkedExercise<E> link){ //将两个节点都指向头 LinkedExercise.Node list=link.head; LinkedExercise.Node p=link.head; //list遍历到最后一个节点 while (list.next!=null){ list=list.next; } //让最后一个节点的写一个指向头结点的下一个 6指向5 list.next=p.next.next; } public static void main(String[] args) { LinkedExercise<Integer> f = new LinkedExercise<>(); f.add(3); f.add(4); f.add(5); f.add(6); f.show(); System.out.println("该链表的长度:" + f.getLenth()); System.out.println(f.lastK(1));//6 System.out.println(f.lastK(4));//3 System.out.println(f.lastK(5));//null f.con(f); System.out.println("环的入口节点:"+f.getLinkCirclrVal()); } }
练习4:合并两个有序的链表(头结点无效时)
包含头结点无效的大多数函数:
class SingleLinekdListTakeHead<E extends Comparable> { protected Node<E> head;//头节点 class Node<E> { protected E data;//数据域 protected Node<E> next;//next引用域 public Node(E data, Node<E> next) { this.data = data; this.next = next; } } //初始化head public SingleLinekdListTakeHead() { head = new Node(new Object(), null); } //在head之后直接插入一个节点,头插法 public void addHead(E element) { Node<E> newNode = new Node(element, null); newNode.next = head.next;//先让新添加的节点的下一个指向原head节点指向的 head.next = newNode;//再让head节点指向新节点 } //尾插法 public void addTail(E element) { Node<E> newNode = new Node(element, null); Node<E> tail = head;//定义一个节点从头走到尾 //tail走到当前链表的尾部 while (tail.next != null) { tail = tail.next; } tail.next = newNode; newNode.next=null; } /** * 固定位置插入一个节点 * 判断参数合法性 * 找到pos位置的前一个节点 * @param pos 固定位置 * @param element 元素 */ public void addPos(int pos, E element) { if (pos <= 0 || pos > getLength()) { return; } Node<E> prev = head.next; int index = 1; while (index++ < pos - 1) { prev = prev.next; } Node<E> newNode = new Node<>(element, null); newNode.next = prev.next; prev.next = newNode; } //删除元素为element的节点 public boolean remove(E element) { //如果只有一个头结点,返回false if (head.next == null) { return false; } //找到该元素所对应的节点 + 该元素所对应的节点的前一个 //从头结点开始遍历 Node<E> tmp = head; while (tmp != null) { if (tmp.next != null && tmp.next.data == element) { //tmp.next是我们要删除的节点 tmp是删除节点的前一个 tmp.next = tmp.next.next; return true; } tmp = tmp.next; } return false; } //设置某个位置的值为newElement public void set(int pos, E newElement){ if(pos <= 0 || pos > getLength()){ return; } //找pos位置的节点 Node<E> tmp = head.next; for(int i=1; i < pos; i++){ tmp = tmp.next; } tmp.data = newElement; } //得到某个元素的值 public E get(E element){ Node<E> tmp = head.next;//从有效节点开始遍历 while(tmp != null){ if(tmp.data == element){ return tmp.data; //找到的话,返回该节点 } tmp = tmp.next; } return null; } //合并两个有序的单链表 public void merge(SingleLinekdListTakeHead<E> list2){ // LinkedExercise<E> list3=new LinkedExercise<>(); Node<E> p=this.head;//最后合并成功的的链表 Node<E> p1=this.head.next;//第一的链表 Node<E> p2=list2.head.next;//第二个链表 while (p1!=null && p2!=null){ if(p1.data.compareTo(p2.data)>=0){ p.next=p2; //list3.add(p2.data); p2=p2.next; }else { p.next=p1; // list3.add(p1.data); p1=p1.next; } p=p.next; } if(p1!=null){ //链表1还有剩余节点 p.next=p1; } if(p2!=null){ p.next=p2; } // return p.data; } //返回长度 public int getLength() { Node<E> tmp = head.next; int length = 0; while (tmp != null) { length++; tmp = tmp.next; } return length; } //打印栈 public String toString() { StringBuilder strs = new StringBuilder(); Node<E> tmp = head.next; while (tmp != null) { strs.append(tmp.data + " "); tmp = tmp.next; } return strs.toString(); //strs是StringBuilder类型,应该添加toString方法,才能返回String类型的 }
//逆置带有头结点的单链表 public void reverse(){ if(head.next==null||head.next.next==null){ return; }else { Node<E> cur=this.head.next.next;//指向第二个有效的节点 this.head.next.next=null; Node<E> pos=null; while (cur!=null){ pos=cur.next;//先将cur.next指向pos cur.next=head.next; head.next=cur;//头插法,将节点插在head后 cur=pos; } } } } public class Linked { public static void main(String[] args) { SingleLinekdListTakeHead<Integer> list=new SingleLinekdListTakeHead(); list.addHead(3); list.addHead(5); list.addHead(8); System.out.println(list.toString());//8 5 3 list.addTail(1); list.addTail(2); list.addTail(4); System.out.println(list.toString());//8 5 3 1 2 4 list.reverse(); System.out.println(list.toString()); // list.addPos(2, 100); //在2 号位置加入元素100 // System.out.println(list.toString()); // list.addPos(0, 1000); // System.out.println(list.toString()); // // list.remove(4); // System.out.println("删除值为4的元素:"+list.toString()); // // list.set(2,2);//true,把2号元素的值改为2 // System.out.println("把2号元素的值改为2:"+list.toString()); // System.out.println(list.get(3)); SingleLinekdListTakeHead list1=new SingleLinekdListTakeHead(); list1.addTail(2); list1.addTail(6); list1.addTail(7); SingleLinekdListTakeHead list2=new SingleLinekdListTakeHead(); list2.addTail(3); list2.addTail(4); list2.addTail(5); list2.addTail(8); list2.addTail(9); list2.addTail(10); list1.merge(list2); System.out.println(list1.toString()); } }
练习5:链式队列
package Exercise; public class LinkedQueue<T> { private Entry<T> front; private Entry<T> rear; private int count; public LinkedQueue(){ this.front=this.rear=new Entry<>(null,null); } class Entry<T>{ T data; Entry<T> next; public Entry(T data,Entry<T> next){ this.data=data; this.next=next; } } public void offer(T data){ Entry<T> node=new Entry<>(data,null); this.rear.next=node; this.rear=node; this.count++; }
/**
*出队列需要判断队列空的情况,头节点无效;如果队列为空,需要将front和rear都指向空
*/ public void poll(){ if(this.front.next!=null){ this.front.next=this.front.next.next; if(this.front.next == null){ this.rear = this.front; } this.count--; } } public int size(){ return this.count; } public T peek(){ return this.front.next.data; } public void show(){ Entry<T> cur=this.front.next; while (cur!=null){ System.out.print(cur.data+" "); cur=cur.next; } System.out.println(); } public static void main(String[] args) { LinkedQueue l=new LinkedQueue(); for (int i = 0; i < 4; i++) { l.offer(i); } l.show(); System.out.println("队头元素为:"+l.peek()); System.out.println("队列长度为:"+l.size()); l.poll(); l.show(); } }
难点:内部类和外部类的构造函数都需要对相应属性做初始化。
对头和尾部节点的初始化:this.front=this.rear=new Entry<>(null,null);
定义一个索引节点,指向头结点,不能用头结点自己去遍历: Entry<T> cur=this.front.next;
需要自定义打印函数show()以及show的实现