链表

之前秋招参加了几家公司的面试,大多问到了基础,数据结构基本都问到了,想了想还是好好复习一下数据结构,现在就从链表开始吧。
链表毋庸置疑,和顺序表是两大基础了,之前在知乎也写过顺序表的博客,以后搬过来。
先看看顺序表结构,顺序表无非就是像数组一样,可以通过下标去访问元素,但是可以设计成比数组更灵活的东西,你可以设计成不断往里面添加元素。还有一些基本操作了,增删改查,但是有些人发现,顺序表去增删改查真的超级费劲,要在插入点之后的每个元素后移,删除元素时每个元素又要前移,当涉及到百万级的顺序表,肯定是处理时间很长的,这样就要采取另一种数据结构了,链表。
链表和数组不一样,不需要连续的地址空间,但是不需要连续的地址空间又怎么让元素之间联系起来呢,在c和c++里面可以给每个元素弄一个指针,这个指针指向的类型就是一个节点,从而这个指针就将一个个元素串起来了,这里用串不合适,因为并不是连续的,而是随机的地址空间联系起来,这样说起来应该更准确。而在java里面就是引用联系元素了,引用可以想象成A和B是同一个东西,共享同一块地址。java里面没有了许多繁杂的概念,指针操作是真的麻烦。这里我就不用c或者c++演示了,直接用java代码。
一些基本操作我就不多说,增删改查,还有面试的时候(当然我没有被问到,别人被问到过),判断是否成环,或者查倒数第几个元素(再没有尾节点的情况下)等等一些相关的链表操作,我都来一一列举.
链表就是除了包含了数据之外还有一个后继,指向下一个节点。
需要注意的是,链表有很多种,单向链表,双向链表,循环链表等等。
以下我就以单链表为例了。
首先就是增删改查
增加我写了两个,一个是添加L型的数据,还有就是添加节点
其他的我相信看代码就可以理解了。

		node<L> newnode=new node<>();
		newnode.data=e;
		tail.next=newnode;
		tail=newnode;
		size++;
	}
	public void addElem(node<L> e) {
		tail.next=e;
		tail=e;
		size++;
	}
	public void del_index(int index) {
		node<L> link=head;
		for (int i=0;i<index-1;i++) {
			link=link.next;
		}
		link.next=link.next.next;
	}
	public void change(int index,L e) {
		node<L> datae=find(index);
		datae.data=e;
	}
	public  node find(int index) {
		node<L> link=head;
		int i=0;
		while(link.next!=null) {
			link=link.next;
			if(index==i) {return link;}
			i++;
		}
		return null;
	}
	public void display() {
		node<L> link=head.next;
		while(link!=null) {System.out.println(link.data); link=link.next;}
	}
	public void insert(list<L> list_inversion,L e,int index) {
		node<L> nodea=head;
		int i=0;
		while(i<index-1) {
			nodea=nodea.next;
			i++;
		}
		node<L> newnode=new node();
		newnode.data=e;
		newnode.next=nodea.next;
		nodea.next=newnode;
		
	}

上面还有一个输出链表的操作,这也是基本操作,我也不多说。

链表的倒置,这里链表的倒置有两种了
简单的就是双向链表的倒置,这个很简单的呗,就是从末节点往前驱找啦。
单链表的话就可能有点麻烦了。就是从第二个节点开始,一个个往头结点后面移动,就像这样:
原链表:head->1->2->3->4->5->6->7
第一次移动:head->2->1->3->4->5->6->7
第二次移动:head->3->2->1->4->5->6->7

最终倒置:head->7->6->5->4->3->2->1
就是这样一步步实现,相信大家都可以的。

	public void inversion() {
	     node<L> nodeA,nodeB;
	     nodeA=head.next;
	     nodeB=nodeA.next;
	     while(nodeB.next!=null) {
	    	 nodeA.next=nodeB.next;
	    	 nodeB.next=head.next;
	    	 head.next=nodeB;
	    	 nodeB=nodeA.next;
	     }
	     nodeB.next=head.next;
	     head.next=nodeB;
	     nodeA.next=null;
	}

交换节点:
这个是我晕晕呵呵的时候写的,可能想法有点傻哈哈哈,就是找到交换节点的前驱然后交换了。

public void swap(list<L> link,node a,node b) {
		//找到前驱
		node<L> apre=null,bpre=null;
		node<L> flag=link.head;
		while(flag!=null&&(apre==null||bpre==null)) {
			if(flag.next==a) {apre=flag;}
			if(flag.next==b) {bpre=flag;}
			flag=flag.next;
		}
		//a 和 b的前驱分别为apre和 bpre
		if(a.next==b) {
			a.next=b.next;
			b.next=a;
			apre.next=b;
		}else if(b.next==a) {
			b.next=a.next;
			a.next=b;
			bpre.next=a;
		}else {
			node c=b.next;
			b.next=a.next;
			a.next=c;
			bpre.next=a;
			apre.next=b;
		}                                         
	}

查找倒数第几个:这个前提是不知道链表长度的情况下。如果是双链表就没有出这个题目的意义了。
首先我们来看看,倒数第n个与最后一个节点的联系,是不是和它相差一定的距离,倒数第3个,和最后一个节点相差两个,倒数第7个,和最后一个节点相差6个,这样就可想而知了,倒数第n个节点,和最后一个节点相差n-1个节点。我们只要从head开始找出相差同样距离的节点,然后以同样的速度往尾节点移动,当后面的节点到达尾节点了,前面那个节点就是我们要找的。
代码如下:

	public void check(list<L> li,int last_index) {
		//设置两个指针差
		if(li.head==li.tail) {System.out.println("没有元素"); return;}
		node<L> first=li.head.next;
		node<L> seconde=first;//此时first是倒数第1个
		for (int i=1;i<last_index;i++) {
			if(seconde.next!=null) {
				seconde=seconde.next;
			}else {
				System.out.println("不存在"+last_index+"倒数!");
				return;
			}
		}
		while(seconde!=li.tail) {
			first=first.next;
			seconde=seconde.next;
		}
		System.out.println(first.data);
		
	}

判断是否成环

这个我们可以想一下初中的相遇问题,在一个操场上,当一个人跑另一个人追,当跑的那个人很快的时候,是不是可以再次追上他,就形成了一个环了。
同样的道理,在链表里面,我们只要用两个移动节点,一个速度快于另一个,这样如果是一个有环的链表的话,快的节点总会追上它,并且到达同一个节点的,这样的话,就可以判断是否成环了。

	public boolean judge_circle(list<L> li) {
		//指针是否追上另一个
		node<L> first=li.head;
		node<L> second=li.head;
		while(true) {
			if(second.next!=null) {
				if(second.next==first) return true;
			}else return false;
			if(second.next.next!=null) {
				if(second.next.next==first) return true;
				second=second.next.next;
			}else return false;
			first=first.next;
		}

	}

当然还有一些其他操作,面试的时候大家都有可能会碰到,当然数据结构知识一部分,还有很多的其他方面的,以防万一,还是理解理解复习复习比较好。
另外我还写了一个双向链表。增加了前驱和后继。
node类:

package bothway_linklist;

public class node {
	node pre;
	int data;
	node next;
}

linklist类:

package bothway_linklist;
//双向链表
public class linklist {
	node head;
	node tail;
	public linklist() {
		head=new node();
		tail=head;
	}
	public void add(int e) {//尾插
		node newnode=new node();
		newnode.data=e;
		tail.next=newnode;
		newnode.pre=tail;
		tail=newnode;
	}
	public void display_x() {
		node link=head;
		while(link.next!=null) {
			link=link.next;
			System.out.println(link.data);
		}
	}
	public void display_t() {
		node link=tail;
		while(link!=head) {
			System.out.println(link.data);
			link=link.pre;
		}
	}
	/*
	 * 
	 *     
	 * 
	 */
	//双向链表增删改查随机插入
	//实现链表合并
	public static void main(String[] args) {
		linklist list=new linklist();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(34);
		list.add(53);
		list.add(32);
		list.add(65);
		list.add(3432);
		list.display_x();
		System.out.println();
		list.display_t();
	}
}


posted @ 2018-11-28 21:48  ongbo  阅读(34)  评论(0)    收藏  举报