单链表的详解以及C语言和JAVA的实现

前文:最近发现基础好重要,学过的容易忘,刷letCode题时候,做到了跟数据结构相关的链表,树才发现,大学学的,都忘了,没办法,选择了代码这条路,就要重新找回来基础,大学不努力,毕业徒伤悲,出来混总是要还的。

最近在看到单链表时候,对指针创建,跟结构体对齐内存等遇到了好多新鲜的知识点,让我想起了abandon这个当初学的第一个词,但是wolf能回头吗?代码都搞不定,以后怎么娶媳妇,怀着这种信念的我才继续坚持的学下去,马上要学到了树的知识,回顾下链表的基础点,做一篇博客,方便以后记忆,我自横刀向天笑,去留肝胆两昆仑!!!

链表:链式存储结构,又称单链表,存储逻辑为一对一的逻辑存储关系。顺序表是按照顺序存储,方便查看,但是链表的存储物理位置是随机的,相互之间靠着指针指向建立联系,如下图所示。

单链表的详解以及C语言和JAVA的实现_第1张图片

图上可以看出,链表存储位置是随机的。

 

链表的节点

链表的数据存储由两部分构成,数据域和指针域。

数据域:数据元素本身所在的区域称为数据域。

直接指向后继元素的指针称为指针域。

下面是我盗来的一个图解

单链表的详解以及C语言和JAVA的实现_第2张图片

链表中每个节点的具体实现,需要使用 C 语言中的结构体,JAVA则需要自己创建类的对象。

C的代码实现如下:

typedef struct Link{
    char elem; //代表数据域
    struct Link * next; //代表指针域,指向直接后继元素
}link; //link为节点名,每个节点都是一个 link 结构体

JAVA则是创建一个类:

 class Node {
	Node next =null;//节点引用,指向下一个节点
	int data;//节点的对象,内容
	
	public Node(int data){
		this.data=data;
	}
}

 

了解了链表数据存储后,看下链表的组成结构。

链表里主要组成为头结点,首元节点,头指针,其他结点。

头指针:一个普通的指针,特点是永远指向链表的第一个结点的位置,头指针用于指明链表的位置,便于后期找到链表并使用表中的数据。

节点:链表中的节点又细分为头节点、首元节点和其他节点:

  • 头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;
  • 首元节点:由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;
  • 其他节点:链表中其他的节点;

结构如图所示

单链表的详解以及C语言和JAVA的实现_第3张图片

注意:链表中有头节点时,头指针指向头节点;反之,若链表中没有头节点,则头指针指向首元节点。

此处头节点是一个比较抽象的东西,它是一个虚拟的节点,可以存在,可以不存在,头节点的主要作用,并不是所谓的标识,而要使链表的操作更加容易,操作方式更加统一。
对于没有表头结点的链表,如果对其插入结点时,需要考虑是在表头插入还是表中插入,需要分不同的情况进行判断。
而如果有了表头结点,执行插入时,该判断可以省去,这样,代码就简化了,而付出的成本,仅仅是固定的一个结点的存储空间。

详情可参考:https://blog.csdn.net/bg2wlj/article/details/52300565

看完链表构成,下面开始用代码进行初始化实现操作。

C语言实现:

链表的创建:

1、声明一个头指针,有必要的话,再声明一个头节点。

2、创建多个存储节点,并建立之间的联系。

 

创建一个含有头节点的链表

 link *initLink(){
 	link *p = (link*)malloc(sizeof(link));//创建一个指针指向头节点
	link *temp=p;//声明一个指针指向头结点,用于遍历链表
	
	for(int i=1;i<5;i++){
		//生成链表后续的一个节点
		link *a=(link*)malloc(sizeof(link));//动态分配内存给这个新节点,也是链表的后续节点 
		//对该结点进行赋值
		a->elem=i;
		a->next=NULL;
		//将头节点指向生成结点,再把生成结点赋值为temp
		temp->next=a;
		temp=a; 
	}
	return p;
 }

创建一个不含有头结点的链表

link * initLink(){
    link * p=NULL;//创建头指针
    link * temp = (link*)malloc(sizeof(link));//创建首元节点
    //首元节点先初始化
    temp->elem = 1;
    temp->next = NULL;
    p = temp;//头指针指向首元节点
    //从第二个节点开始创建
    for (int i=2; i<5; i++) {
     //创建一个新节点并初始化
        link *a=(link*)malloc(sizeof(link));
        a->elem=i;
        a->next=NULL;
        //将temp节点与新建立的a节点建立逻辑关系
        temp->next=a;
        //指针temp每次都指向新链表的最后一个节点,其实就是 a节点,这里写temp=a也对
        temp=temp->next;
    }
    //返回建立的节点,只返回头指针 p即可,通过头指针即可找到整个链表
    return p;
}

后续插入,删除操作以有头节点为例

插入结点

 link *insertElem(link *p,int elem,int add){
  	link *temp=p;
  	//插入时候,要考虑到是头插法还是中途插入,尾插法,头插法直接写在头部插入实现,尾插法则找到最后一个下标
	 //此处循环可以满足三种实现
	 for(int i=1;inext;
	 } 
	 link *a=(link*)malloc(sizeof(link));
  	  a->elem=elem;
  	  a->next=temp->next;//插入的节点指向临时定义结点的下一个节点 
  	  temp->next=a;
  	  return p;
  } 

删除节点

 link *delElem(link *p,int add){
 	link *temp = p;//定义一个临时结点,找到要删除的节点 
 	for(int i=1;inext;
	 } 
	 
	 link *del = temp->next;//定义一个指针指向要被删除的元素
	 temp->next= temp->next->next;
	 free(del);//释放资源
	 return p; 
 	
 	
 } 

查找节点

 int selectElem(link *p,int elem){
 	link *temp=p;
 	int i=1;
 	while(temp->next){
 		temp=temp->next;
 		if(temp->elem==elem){
 			return i;
		 }
		 i++;
	 }
 	
 	return -1; 	
 }

 

更新节点

 link *updateElem(link *p,int add,int elem){
 	link * temp=p;
    temp=temp->next;//在遍历之前,temp指向首元结
 	for(int i=1;inext;
	 }
	 temp->elem=elem;
	 
	 return p;
 } 

 

输出链表的值

 void display(link *p){
    link* temp=p;//将temp指针重新指向头结点
    //只要temp指针指向的结点的next不是Null,就执行输出语句。
    while (temp->next) {
        temp=temp->next;
        printf("%d ",temp->elem);
    }
    printf("\n");
}

 

在main函数中调用查看输出即可

typedef struct Link{
	int elem;
	struct Link *next;
}link;
//初始化节点 
 link *initLink();
 
 //链表插入函数,p是链表,elem是插入结点的数据域,add是插入位置
 link *insertElem(link *p,int elem,int add); 
 //p是原链表,delete是删除的下标元素 
 link *delElem(link *p,int add); 
 //查找元素 ,p是原链表,elem是要查找的元素 
  int selectElem(link *p,int elem);
 //更新元素 
 link *updateElem(link *p,int add,int elem);
 
 void display(link *p);//打印 
 
 int main(){
 	 printf("初始化链表为:\n");
  	link *p=initLink();
    display(p);
     printf("在第4的位置插入元素5:\n");
     p = insertElem(p,5,4);
      display(p);
    printf("删除3位置上的元素:\n");
    p = delElem(p,3);
    display(p);
 	printf("查找元素2的位置为:\n");
    int address=selectElem(p, 2);
    if (address==-1) {
        printf("没有该元素");
    }else{
        printf("元素2的位置为:%d\n",address);
    }
    printf("更改第3的位置上的数据为7:\n");
    p=updateElem(p, 3, 7);
    display(p);
 	return 0;
 }
link *initLink(){
 	link *p = (link*)malloc(sizeof(link));//创建一个指针指向头节点
	link *temp=p;//声明一个指针指向头结点,用于遍历链表
	
	for(int i=1;i<5;i++){
		//生成链表后续的一个节点
		link *a=(link*)malloc(sizeof(link));//动态分配内存给这个新节点,也是链表的后续节点 
		//对该结点进行赋值
		a->elem=i;
		a->next=NULL;
		//将头节点指向生成结点,再把生成结点赋值为temp
		temp->next=a;
		temp=a; 
	}
	return p;
 }
 
  link *insertElem(link *p,int elem,int add){
  	link *temp=p;
  	//插入时候,要考虑到是头插法还是中途插入,尾插法,头插法直接写在头部插入实现,尾插法则找到最后一个下标
	 //此处循环可以满足三种实现
	 for(int i=1;inext;
	 } 
	 link *a=(link*)malloc(sizeof(link));
  	  a->elem=elem;
  	  a->next=temp->next;//插入的节点指向临时定义结点的下一个节点 
  	  temp->next=a;
  	  return p;
  } 
 
 link *delElem(link *p,int add){
 	link *temp = p;//定义一个临时结点,找到要删除的节点 
 	for(int i=1;inext;
	 } 
	 
	 link *del = temp->next;//定义一个指针指向要被删除的元素
	 temp->next= temp->next->next;
	 free(del);//释放资源
	 return p; 
 	
 	
 } 
 
 int selectElem(link *p,int elem){
 	link *temp=p;
 	int i=1;
 	while(temp->next){
 		temp=temp->next;
 		if(temp->elem==elem){
 			return i;
		 }
		 i++;
	 }
 	
 	return -1; 	
 }
 
 link *updateElem(link *p,int add,int elem){
 	link * temp=p;
    temp=temp->next;//在遍历之前,temp指向首元结
 	for(int i=1;inext;
	 }
	 temp->elem=elem;
	 
	 return p;
 } 
 void display(link *p){
    link* temp=p;//将temp指针重新指向头结点
    //只要temp指针指向的结点的next不是Null,就执行输出语句。
    while (temp->next) {
        temp=temp->next;
        printf("%d ",temp->elem);
    }
    printf("\n");
}

输出结果如下

单链表的详解以及C语言和JAVA的实现_第4张图片

 

如果用JAVA实现如下:

添加节点:

	public void addNode(int num){
		Node newNode = new Node(num);
		if(head == null){
			head = newNode;
			return;
		}
		Node node2 = head;
		while(node2.next!=null){
			node2 = node2.next;
		}
		node2.next =newNode;
	}

删除指定下标的节点

	//删除指定下标位置的节点
	public  void deleteNode(int index){
		if(index<0 || index>length()){
			System.out.println("index 不准确");
		}
		if(index == 1){
			head = head.next;
		}
		Node temp = null;
		for(int i=1;i

更新节点

//更新结点
	public void updateNode(int index,int data){
		if(index<0 || index>length()){
			System.out.println("index 不准确");
		}
		if(index == 1){
			head = head.next;
		}
		Node temp = null;
		for(int i=1;i

 

获取长度

//获取长度
	public  int length(){
		int length=0;
		Node tmp = head;
		while(tmp.next!=null){
			length++;
			tmp = tmp.next;
		}
		
		return length;
		
	}

 

 

创建则有所不同,需要add节点进去。(目前没想到怎么一个for循环实现的链表的操作,有知道得请留言讨论)

TestLink list = new TestLink();
	        list.addNode(5);
	        list.addNode(3);
	        list.addNode(1);
	        list.addNode(2);
	        list.addNode(55);
	        list.addNode(36);
	        System.out.println("linkLength:" + list.length());
	        System.out.println("head.data:" + list.head.data);

PS:链表涉及到指针,比较抽象,本Wolf还在逐步理解中,此处一篇分享加深下印象,C语言的更适合理解,边画图边实现,我手写了两遍,对语法的不熟悉,还是容易在C创建节点,创建头指针等地方忽略到,思想一定要学到,本篇有错误之处,请及时指出,才发现这种底层的东西,难理解,还费事,主要还是我菜,有不懂的可以加QQ396301446,一起讨论,共同学习进步,选择看数据结构,就要做好被虐准备,我自横刀向天笑,去留肝胆两昆仑。

 

 

 

 

你可能感兴趣的:(胖胖狼,fat,wolf,数据结构)