1. 链表
链表作为最基本的数据结构,存储特点:可以用任意一组存储单元来存储单链表中的数据元素(即存储单元可以是不连续的),其中一个结点(数据域,地址域),数据域:数据元素,地址域:链表元素的前驱或后继元素的地址。一个线性链表必须使用一个头指针记住元素a0的节点地址。
时间复杂度:1)O(1):isEmpty;
O(n):toString();size();get(i);set(i);insert(i,x);remove(i);
search(key);remove(key);
2)插入或删除后继点的时间是O(1),插入前驱点或删除自己的时间是O(n)
3)插入或删除的时间花在查询上。
2. 如何从链表中删除重复数据
publicclass myLinkedList {
Node<String>head =null;//链表头的引用
/**
* 插入数据内容:新节点
* @param d
*/
publicvoid addNode(String d){
Node<String>newNode=newNode<String>(d);
if(head==null){
head=newNode;
return;
}
Node<String>tmp=head;
while(tmp.next!=null){
tmp=tmp.next;
}
tmp.next=newNode;
}
publicvoid printList(){
Node<String>tmp=head;
while(tmp!=null){
System.out.print(tmp.data+" ");
tmp=tmp.next;
}
}
/**
* 删除重复的字符元素
* @param head
*/
publicvoid deleteDuplecate(){
Hashtable<String,Integer>table=newHashtable<String,Integer>();
Node<String>tmp=head;
Node<String>pre=null;
while(tmp!=null){
if(table.containsKey(tmp.data))
pre.next=tmp.next;
else{
table.put(tmp.data, 1);
pre=tmp;
}
tmp=tmp.next;
}
}
publicstatic void main(String[] args) {
myLinkedListlist=newmyLinkedList();
list.addNode("a");
list.addNode("e");
list.addNode("e");
list.addNode("f");
list.addNode("a");
list.addNode("g");
list.printList();
System.out.println(" ");
list.deleteDuplecate();
list.printList();
}
}
这种删除重复元素的方法优点是时间复杂度较低,但是缺点是在遍历过程中需要额外的存储空间来保存已遍历过的值。
另外还用一种方法,不需要额外的存储空间,但是时间复杂度较高。主要思路:对链表进行双重循环遍历,外循环正常遍历链表,假设外循环当前遍历的节点为cur,内循环从cur开始遍历,若碰到与cur所指向节点值相同,则删除这个重复节点。
publicvoid deleteDuplecate2(){
Node<String>p=head;
while(p!=null){
Node<String>q=p;
while(q.next!=null){
if(p.data==q.next.data){
q.next=q.next.next;
}else
q=q.next;
}
p=p.next;
}
}
3. 如何找出单链表中的倒数第k个元素
1) 改进后的一种方法:从头至尾的方向从链表中的某个元素开始,遍历k个元素后刚好达到链表尾,那个该元素就是要找的倒数第k个元素,此方法要对同一批元素进行反复多次的遍历.算法复杂度为O(kn),效率太低。
2) 另一种高效的方式:从头到尾进行遍历查表,设置两个指针,让其中一个指针比另一个指针先前移k-1步,然后两个指针同时向前遍历。知道先行的指针值为NULL是,另一个指针所指的位置就是要找的倒数第k个位置。
public Node<String> findElem(int k){
if(k<1||k>this.length())
returnnull;
Node<String>p1=head;
Node<String>p2=head;
for(int i=0;i<k;i++){???前移k-1步
p1=p1.next;
}
while(p1!=null){
p1=p1.next;
p2=p2.next;
}
return p2;
}
4. 如何实现链表的反转
例如有三个相邻的点1,2,3,假如经过若干步操作,已经把结点1之前的指针调整完毕,这些结点的next指向前面一个结点。现在遍历到结点2,需要调整结点的next指针,让它指向结点1,需要注意的是一旦调整了指针的指向,链表就断开了,因为已经没有指针指向结点3了,链表就断了。所以需要在调整2的next之前将3保存下来。下面给出非递归方式实现链表的反转。
publicvoid ReverseIteratively(Node<String> head){
Node<String>pReversedHead=head;//反转后的头结点
Node<String>pNode=head;//用于遍历的指针
Node<String>pPrev=null;//用于保存结点的后继结点
while(pNode!=null){
Node<String>pNext=pNode.next;
if(pNext==null)
pReversedHead=pNode;//如果链表为空,则反转后的链表即为头结点pNode
pNode.next=pPrev;//第一次循环时当前结点next指向空,后面指向的自然就是前驱结点了
pPrev=pNode;//保存当前结点
pNode=pNext;//后移一个结点
}
this.head=pReversedHead;//遍历完毕head指向PNode(遍历的最后一个结点)
}