- 链表
链表是一种基本的一维离散存储数据结构。相对于数组,它是离散内存的,不能如数组一样通过下标来查询相应地数据,每一个链表节点只能知道它的上一个(双端链表)和它的下一个(单链表,双端链表)节点。C语言通过malloc/free控制内存,C++里通过new/delete,Java则是只有new对象。
1.Java链表节点定义
class Node{
private Node next;
private int data;
public Node(){
}
public Node(int data){
this.data=data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
2.链表操作类
class LinkedList{
private Node first;
public Node getfirst() {
return first;
}
public void setfirst(Node first) {
this.first = first;
}
//按值搜寻第一个等于目标的节点
public Node findNode(int target){
if(first==null){
System.out.println("initialise the first");
return null;
}
Node p=first;
while(p.getData()!=target && p!=null)
p=p.getNext();
return p;
}
public Node findPreNode(Node target){
if(first==null){
System.out.println("initialise the first");
return null;
}
Node p=first;
while(p.getNext()!=target)
p=p.getNext();
return p;
}
//返回链表下标查找
public int IndexOfNode(int target){
int index=1;
if(first==null){
System.out.println("initialise the first");
return -1;
}
Node p=first;
while(p.getData()!=target)
{
index++;
p=p.getNext();
if(p==null)
{
index=-1;
break;
}
}
return index;
}
//在末尾添加节点
public void addNode(Node add){
if(first==null){
first=add;
add.setNext(null);
return;
}
Node p=first;
while(p.getNext()!=null)
p=p.getNext();
p.setNext(add);
add.setNext(null);
}
//头插法生成链表
public void headAdd(Node node){
if(first == null){
first=node;
node.setNext(null);
}else{
node.setNext(first);
first=node;
}
}
//删除节点--须指定链表中的节点
public void deleteNode(Node toDel){
if(first==null){
System.out.println("initialise the first");
return ;
}
Node p=toDel.getNext();
if(p!=null)
{
toDel.setNext(p.getNext());
toDel.setData(p.getData());
p.setNext(null);
}else{
deleteEndNode(toDel);
}
}
//删除末尾
public void deleteEndNode(Node toDel){
Node p=first;
while(p.getNext()!=toDel && p!=null)
p=p.getNext();
p.setNext(null);
}
//常规删除
public void deleteNormal(Node toDel){
Node p=findPreNode(toDel);
p.setNext(toDel.getNext());
toDel.setNext(null);
}
//修改一个节点的值
public void update(Node target,int value){
if(first==null){
System.out.println("initialise the first");
return ;
}
target.setData(value);
}
public void printList(){
if(first==null){
System.out.println("initialise the first");
return ;
}
Node p=first;
while(p!=null){
System.out.print(p.getData()+" ");
p=p.getNext();
}
System.out.println();
}
}
小记:(1) 生成单链表可以有头插法和尾插法,尾插法的添加节点为O(n)复杂,删除也是O(n);头插法添加则是O(1),删除还是O(n)。
(2) 删除方法有一个是O(1)复杂度的。方法:给定一个节点,删除它下一个节点并把下一个节点的值转移到给定节点。
- 问题1:删除链表内重复的节点。(数据重复)
因为使用的是那个O(1)删除的方法,所以写得有些奇怪。总体方法就是使用一个O(n)空间来存储节点,发现重复就删除,遍历时间复杂就只用O(n)。
//删除未排序链表重复节点1 缓冲区版
public void DelRepeated1(LinkedList list){
HashMap<Integer,Boolean> buffer=new HashMap<Integer,Boolean>();
Node p=list.getfirst();
while(p!=null){
System.out.println("data="+p.getData());
if(buffer.get(p.getData())==null){
buffer.put(p.getData(), true);
}
else
{
list.deleteNode(p);
while(true){
if(buffer.get(p.getData())!=null)
list.deleteNode(p);
else
break;
}
}
p=p.getNext();
}
}
若不使用缓冲区,则只能使用暴力遍历法,从当前节点开始到表尾“排除异己”,“步步为营”。复杂度O(n*n)。
并且删除方法是使用O(n)复杂度的。
//删除未排序俩表重复节点2 无缓冲区
public void DelRepeated2(LinkedList list) {
Node p = list.getfirst();
Node q = list.getfirst().getNext();
while(p!=null){
q = p.getNext();
while(q!=null){
if(p.getData()==q.getData())
list.deleteNormal(q);
q=q.getNext();
}
p = p.getNext();
}
}
- 问题2:寻找倒数低k个节点。
这个问题其实挺简单,如果链表是你自建的,那么添加个length或size属性记录那么解决这个问题就方便多了。如果仅给定一个单链表,则遍历一次找出length再以互补或互余的思维来求出倒k的下标。
//寻找倒数第k个节点
public Node findNK(LinkedList list,int k){
if(k<=0)
{
System.out.println("k should be upper than 0");
return null;
}
int n=0;
Node p=list.getfirst();
while(p!=null)
{
n++;
p=p.getNext();
}
int result=n-k;
if(n-k<0)
{
System.out.println("index out of range");
return null;
}
p=list.getfirst();
while(result!=0){
p=p.getNext();
result--;
}
return p;
}
- 问题3:给定一个值x,将链表分割成两部分,大于x部分和小与x部分。
接到这道题的时候,我马上蹦出一个想法:排序。排完序就解决了。最快的排序算法也是O(n*logn)。然后想到快速排序,快速排序的partition函数可以一次将数组的分割成以基准元素为分界点的序列。这么一想,这道题可以做成O(n)复杂度了。实现它需要的数据结构为双端链表。
//双端链表分割
public void DivideLink(DoubleLink dl,int element){
DoubleNode tail=dl.getEnd().getPre();
DoubleNode first=dl.getFirst();
first.setData(element);
int start=1;
int end=dl.getLength();
int x=element;
while(start<end){
while(start<end && tail.getData()>=x)
{
end--;
tail=tail.getPre();
}
if(start<end){
start++;
first.setData(tail.getData());
first=first.getNext();
}
while(start<end && first.getData()<=x)
{
start++;
first=first.getNext();
}
if(start<end){
tail.setData(first.getData());
end--;
tail=tail.getPre();
}
}
first.setData(x);
}
循环链表的实现:
class DoubleLink{
DoubleNode first;
DoubleNode end;
DoubleNode current;
int length=0;
//初始化空双端链表
public DoubleLink(){
first=new DoubleNode();
end=new DoubleNode();
first.setNext(end);
end.setNext(first);
first.setPre(end);
end.setPre(first);
current=first;
}
public int getLength(){
return length;
}
public DoubleNode getFirst() {
return first;
}
public void setFirst(DoubleNode first) {
this.first = first;
}
public DoubleNode getEnd() {
return end;
}
public void setEnd(DoubleNode end) {
this.end = end;
}
public void InsertNode(DoubleNode node){
current.setNext(node);
node.setPre(current);
current=node;
end.setPre(node);
node.setNext(end);
length++;
}
public void RemoveNode(DoubleNode toDel){
DoubleNode p = toDel.getPre();
DoubleNode q = toDel.getNext();
p.setNext(q);
q.setPre(p);
toDel.setPre(null);
toDel.setNext(null);
// if (toDel != first && toDel != end) {
// p.setNext(q);
// q.setPre(p);
// }else if(toDel == first){
// first=q;
// p.setNext(q);
// q.setPre(p);
// }else if(toDel == end){
// end=p;
// p.setNext(q);
// q.setPre(p);
// }
length--;
}
public void UpdateNode(DoubleNode toUpdate,int data){
toUpdate.setData(data);
}
public DoubleNode SearchNode(int data){
DoubleNode p=first.getNext();
while(p.getData()!=data && p!=end)
p=p.getNext();
if(p==end)
p=null;
return p;
}
public int IndexOfNode(DoubleNode search){
int index=0;
DoubleNode p=first;
while(p!=end && p!=search){
p=p.getNext();
index++;
}
if(p==end)
index=-1;
return index;
}
public void printLink(){
DoubleNode p=first.getNext();
while(true){
if(p==end)
break;
System.out.print(p.getData() + " ");
p = p.getNext();
}
System.out.println();
}
}
class DoubleNode{
private int data;
private DoubleNode pre;
private DoubleNode next;
public DoubleNode(){
}
public DoubleNode(int data){
this.data=data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DoubleNode getPre() {
return pre;
}
public void setPre(DoubleNode pre) {
this.pre = pre;
}
public DoubleNode getNext() {
return next;
}
public void setNext(DoubleNode next) {
this.next = next;
}
}
- 问题四:链表加法,每一个节点为一个数位,分别分两种方法来实现,高位到低位、低位到高位。
//链表加法
public LinkedList ListAdd1(LinkedList list1,LinkedList list2){
Node current1=list1.getfirst();
Node current2=list2.getfirst();
LinkedList result=new LinkedList();
int carry=0;
int count;
result.setfirst(new Node(-1));
while(current1 != null && current2 != null){
count=carry+current1.getData()+current2.getData();
carry=count/10;
count=count%10;
Node reNode=new Node(count);
result.addNode(reNode);
current1=current1.getNext();
current2=current2.getNext();
}
while(current1 != null){
Node reNode=new Node(current1.getData()+carry);
result.addNode(reNode);
current1=current1.getNext();
carry=0;
}
while(current2 != null){
Node reNode=new Node(current2.getData()+carry);
result.addNode(reNode);
current1=current2.getNext();
carry=0;
}
if(carry!=0){
Node reNode=new Node(carry);
result.addNode(reNode);
}
return result;
}
public LinkedList ListAdd2(LinkedList list1, LinkedList list2) {
int number1 = 0;
int number2 = 0;
Node f1 = list1.getfirst();
Node f2 = list2.getfirst();
LinkedList result = new LinkedList();
while (f1 != null) {
number1 = number1 * 10 + f1.getData();
f1 = f1.getNext();
}
while (f2 != null) {
number2 = number2 * 10 + f2.getData();
f2 = f2.getNext();
}
int count = number1 + number2;
int rank=1;
int temp=count;
while(temp>10)
{
temp=temp/10;
rank=rank*10;
}
temp=count;
while (rank != 0) {
result.addNode(new Node(temp/rank));
temp%=rank;
rank/=10;
}
return result;
}
- 问题五:链表查环,从哪个节点开始成环的,输出节点。
这个解法还是hashmap。
//链表查环
public Node findCircle(LinkedList list){
HashMap<Node,Boolean> map=new HashMap<Node,Boolean>();
Node p=list.getfirst();
while(true){
if(map.get(p)==null)
map.put(p, true);
else
break;
System.out.println(p.getData());
p=p.getNext();
}
return p;
}
- 问题六:链表回文检测。每个数据位存储字符,一个字符串是否回文。
关键点是使用栈来保存状态,到中点的时候开始pop,如果发现不是回文,则返回false。
//回文检测
public <T> boolean LinkedPalindrome(ArrayList<T> list){
boolean isPalindrome=true;
ArrayList<T> stack=new ArrayList<T>();
int i=-1;
int isEven=list.size()%2;
int mid=list.size()/2;
System.out.println("mid="+mid);
for(int j=0;j<list.size();j++){
T data=list.get(j);
if(j<mid)
{
stack.add(data);
i++;
}
else {
if (isEven == 0) {
if(data==stack.get(i))
{
i--;
continue;
}else
{
isPalindrome=false;
break;
}
}else if(isEven == 1){
if(j==mid)
continue;
else{
if(data==stack.get(i))
{
i--;
continue;
}else{
isPalindrome=false;
break;
}
}
}
}
System.out.println("j="+data+" i="+stack.get(i));
}
return isPalindrome;
}
- 问题七:链表找环
把我以前的代码复制过来了,追赶法。
bool IfCircle(LinkList *root) { /* 追赶式验证是否存在环 */ LinkList *fast=root; LinkList *slow=root; while(fast && slow) { fast=fast->p_next; fast=fast->p_next; if(fast==slow) return true; slow=slow->p_next; } return false; }