数组
1 是顺序存储的相同数据类型的数据结构,在内存空间上地址也是连续的,所以在声明数组的时候就要确定长度
声明数组
静态创建数组
int array[100];//定义一个大小100的数组,没有初始化
int array[2]= {1,2};//定义长度为2的数组,并初始化
int array[20]= {1,2};//定义长度为20的数组,并初始化前两个
new动态创建:使用new分配一个数组,返回指向第一个元素的指针
int* array = new int[100]; delete []array;//分配长度为100的数组并删除
int* array = new int[100](1,2); delete []array;//分配长度为100的数组并初始化前两个元素,再删除
也可用malloc:
int a[100] = malloc(sizeof(int) * 100);
字符串数组 char b[] = “abd”;//定义大小为4的字符数组,因为字符串结尾以‘\0’结束
字符数组 char b[] = {'a','b'};//定义大小为2的字符数组
2 数组可以通过下标访问元素,速度很快,但是同样插入或者删除元素就很困难,需要移动大量的元素
3 数组存在下标越界问题,下标的范围应该大于等于0,小于数组的大小
链表
1 物理存储单元上不连续,非顺序的存储结构
2 数据的逻辑顺序是通过指针链接次序实现的,每个节点包括数据域和指针域。空间开销大
3 长度不固定,可以任意的增加删除元素,但是不能做到随机访问
数组缺点需要预先知道数据大小,链表缺点不能像数组一样随机读取,增加结点指针域,空间开销大,优点方便插入跟删除任意位置结点
数组和链表有以下几点不同:
- 存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个结点要保存相邻结点指针。
- 数据查找: 数组的线性查找速度快, 查找操作直接使用偏移地址。 链表需要按顺序检索结点,效率低。
- 数据插入或删除。链表可以快速插入和删除结点, 而数组则可能需要大量数据移动
- 越界问题: 链表不存在越界问题,数组有越界问题。
说明:一定要根据实际需求进行选择。数组便于查询(定位访问),链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。
struct ListNode
{
int val;//数据域
ListNode *next;//指针域,指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {}//初始化当前节点值为x, 指针为空(这一行是构造函数)
};
数组和链表的应用场景(待补充,又看到这篇文章的可以一起补充啊,互相交流)
单链表
//头插法:每个新节点都插入到头结点后面,第一个插进去的是尾节点,所以打印出来是倒序
newNode->data = x;
newNode->next = headNode->next;
headNode->next = newNode;
//尾插法:每个新节点都插入到这个链表的后面,所以多一个始终指向表尾的指针
ListNode *rear;
rear = headNode;//一开始头结点是表尾
newNode->data = x;
rear->next = newNode;
rear = newNOde;//rear始终指向表尾
所有节点插入后,循环之后,rear->next = NULL;
插入节点
链表不要断,先让新节点的next指向要插入的那个节点,然后让那个节点的前驱节点的next指向新节点
p->next = cur->next;//p是新节点,cur是要插入位置的前驱节点
cur->next = p;
双链表
每个节点都有两个指针,分别指向直接前驱和直接后继,相比单链表更方便找到直接前驱节点和后继节点
循环链表(待补充)
单链表逆置,反转链表
循环算法,迭代,思路就是就地箭头转过来完整代码参见单链表逆序
ListNode* reverseList(ListNode* head) {
if (head == NULL) return head;//注意一定要考虑指针是否为空,不然就是思路不周祥
ListNode* pre = NULL;
ListNode* cur = head;
while (cur) {
ListNode* p = cur->next;//cur跟p依次后移
cur->next = pre;//箭头转过来,类似交换两数的
pre = cur;
cur = p;
}
return pre;}
递归
ListNode* reverseList(ListNode* head) {
if (head == NULL || head->next == NULL) return head;
ListNode* p = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return p;
链表合并
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == NULL) return l2;
if(l2 == NULL) return l1;
ListNode *dummy = new ListNode(0);
ListNode *head = dummy;
while(l1 && l2){
if(l1 ->val < l2 ->val){//升序,那个数字小,就指向哪个
head->next = l1;
l1 = l1->next;
}
else{
head ->next = l2;
l2 =l2->next;
}
head = head->next;
}
head ->next = l1 ? l1 : l2;
return dummy->next;}
判断一个链表是否有环博客判断链表是否有环
链表有环找到环的起始点(待补充)
list数据结构
list是由双向链表实现的,因此内存空间是不连续的。只能通过指针访问,所以list的随机存取效率低,时间复杂度为o(n);
但由于链表的特点,能高效地进行插入和删除。
手写代码:(待补充)
1、一个单向链表,每个节点上都有一个value;给定 一个 value,将该 value 之前的所有 value返回
7、手写代码:将一个数组指定offset后面的元素前置,可采用链表实现
8、手写代码:循环链表插入元素