双链表的实现

基于go语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package main

import "fmt"

// Node 定义双链表节点
type Node struct {
data interface{}
prev *Node
next *Node
}

// DoublyLinkedList 定义双链表
type DoublyLinkedList struct {
head *Node
tail *Node
size int
}

// InsertTail 向链表尾部插入节点
func (dll *DoublyLinkedList) InsertTail(data interface{}) {
newNode := &Node{data: data}
if dll.size == 0 {
dll.head = newNode
dll.tail = newNode
} else {
dll.tail.next = newNode
newNode.prev = dll.tail
dll.tail = newNode
}
dll.size++
}

// InsertHead 向链表头部插入节点
func (dll *DoublyLinkedList) InsertHead(data interface{}) {
newNode := &Node{data: data}
if dll.size == 0 {
dll.head = newNode
dll.tail = newNode
} else {
newNode.next = dll.head
dll.head.prev = newNode
dll.head = newNode
}
dll.size++
}

// Remove 删除节点
func (dll *DoublyLinkedList) Remove(node *Node) {
if node.prev != nil {
node.prev.next = node.next
} else {
dll.head = node.next
}
if node.next != nil {
node.next.prev = node.prev
} else {
dll.tail = node.prev
}
node.prev = nil
node.next = nil
dll.size--
}

// Display 打印双链表
func (dll *DoublyLinkedList) Display() {
curr := dll.head
for curr != nil {
fmt.Print(curr.data, " ")
curr = curr.next
}
fmt.Println()
}

func main() {
dll := DoublyLinkedList{}
dll.InsertTail(1)
dll.InsertTail(2)
dll.InsertHead(0)
dll.Display() // 应该输出:0 1 2

// 删除节点
headNode := dll.head
dll.Remove(headNode)
dll.Display() // 应该输出:1 2
}

基于c语言实现双链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdio.h>
#include <stdlib.h>

// 定义双链表节点结构
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
// 创建一个新节点
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("内存分配失败");
exit(1);
}
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
// 在双链表前插入节点
void insertFront(Node** head, int data) {
Node* newNode = createNode(data);
newNode->next = *head;
if (*head != NULL) {
(*head)->prev = newNode;
}
*head = newNode;
}

// 在双链表后插入节点
void insertRear(Node** head, int data) {
Node* newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
return;
}
Node* last = *head;
while (last->next != NULL) {
last = last->next;
}
last->next = newNode;
newNode->prev = last;
}
// 删除双链表前的节点
void deleteFront(Node** head) {
if (*head == NULL) {
printf("链表为空,无法删除");
return;
}
Node* temp = *head;
*head = (*head)->next;
if (*head != NULL) {
(*head)->prev = NULL;
}
free(temp);
}
// 删除双链表后的节点
void deleteRear(Node** head) {
if (*head == NULL) {
printf("链表为空,无法删除");
return;
}
Node* secondLast = *head;
while (secondLast->next->next != NULL) {
secondLast = secondLast->next;
}
Node* temp = secondLast->next;
secondLast->next = NULL;
free(temp);
}
// 打印双链表
void printList(Node* node) {
while (node != NULL) {
printf("%d ", node->data);
node = node->next;
}
printf("\n");
}
int main() {
Node* head = NULL;

insertFront(&head, 10);
insertFront(&head, 20);
insertRear(&head, 30);
insertRear(&head, 40);

printList(head); // 输出:20 10 30 40

deleteFront(&head);
deleteRear(&head);

printList(head); // 输出:10 30

return 0;
}

区别:

c语言的双链表 我们要清楚 改变了头节点 所以要传入二级指针 才能正确的记录到头指针的变化

而Go语言,因为 Go 中的切片、映射和通道等是引用类型,它们在函数参数传递时本身就是按引用传递的,不需要通过指针的指针(二级指针)来实现修改。这实际上是 Go 语言的一个特性:切片、映射和通道在函数间传递时,共享底层数据结构,因此不需要额外的二级指针。

栈的c语言实现

1.数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<stdio.h>
#include<stdbool.h>

typedef struct Stack {
int data[10];
int top;
} Stack;

void InitStack(Stack* stack) {
stack->top = -1;
}

bool IsFull(Stack* stack) {
return stack->top == 9;
}

bool IsEmpty(Stack* stack) {
return stack->top == -1;
}

bool push(Stack* stack, int data) {
if (IsFull(stack)) {
printf("栈已经满了不能执行入栈操作\n");
return false;
}
stack->data[++stack->top] = data;
return true;
}

int pop(Stack* stack) {
if (IsEmpty(stack)) {
printf("栈已经空了不能出栈\n");
return -1;
}
return stack->data[stack->top--];
}

void Println(Stack* stack) {
printf("栈中元素:");
for (int i = stack->top; i >= 0 ; i--) {
printf("%d ", stack->data[i]);
}
printf("\n");
}

int main() {
Stack stack;
InitStack(&stack);

push(&stack, 1);
push(&stack, 2);
push(&stack, 3);
push(&stack, 4);

Println(&stack);

//pop(&stack);
pop(&stack);
pop(&stack);
// pop(&stack);

Println(&stack);
return 0;
}

2.链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct stack {
int data;
struct stack* next;
} Stack;

void InitStack(Stack** top) {
*top = NULL; // 初始化为空链表,头指针为NULL
}

bool IsEmpty(Stack* top) {
return top == NULL;
}

Stack* CreateNode(int data) {
Stack* node = (Stack*)malloc(sizeof(Stack));
if (node == NULL) {
printf("分配空间出现错误");
return NULL;
}
node->data = data;
node->next = NULL;
return node;
}

void Push(Stack** top, int data) {
Stack* newnode = CreateNode(data);
if (newnode == NULL)
return;
newnode->next = *top;
*top = newnode; // 更新头指针指向新节点
}

int Pop(Stack** top) {
if (IsEmpty(*top)) {
printf("栈已经空了不能出栈\n");
return -1;
}
Stack* temp = *top;
int data = temp->data;
*top = temp->next; // 更新头指针指向下一个节点
free(temp);
return data;
}

void Println(Stack* top) {
Stack* temp = top;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}

int main() {
Stack* stack;
InitStack(&stack);
Push(&stack, 1);
Push(&stack, 2);
Push(&stack, 3);
Push(&stack, 4);


Println(stack);

printf("出栈: %d\n", Pop(&stack));
printf("出栈: %d\n", Pop(&stack));

Println(stack);

return 0;
}

队列c语言实现

数组实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct stack {
int data;
struct stack* next;
} Stack;

void InitStack(Stack** top) {
*top = NULL; // 初始化为空链表,头指针为NULL
}

bool IsEmpty(Stack* top) {
return top == NULL;
}

Stack* CreateNode(int data) {
Stack* node = (Stack*)malloc(sizeof(Stack));
if (node == NULL) {
printf("分配空间出现错误");
return NULL;
}
node->data = data;
node->next = NULL;
return node;
}

void Push(Stack** top, int data) {
Stack* newnode = CreateNode(data);
if (newnode == NULL)
return;
newnode->next = *top;
*top = newnode; // 更新头指针指向新节点
}

int Pop(Stack** top) {
if (IsEmpty(*top)) {
printf("栈已经空了不能出栈\n");
return -1;
}
Stack* temp = *top;
int data = temp->data;
*top = temp->next; // 更新头指针指向下一个节点
free(temp);
return data;
}

void Println(Stack* top) {
Stack* temp = top;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}

int main() {
Stack* stack;
InitStack(&stack);
Push(&stack, 1);
Push(&stack, 2);
Push(&stack, 3);
Push(&stack, 4);


Println(stack);

printf("出栈: %d\n", Pop(&stack));
printf("出栈: %d\n", Pop(&stack));

Println(stack);

return 0;
}

哈希表c简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<stdio.h>
#include<stdlib.h>

#define NUM 5

typedef struct HashList {
int num;
char data[NUM]; // 修改为字符数组
} HashList;

HashList* initList() {
HashList* list = (HashList*)malloc(sizeof(HashList));
list->num = 0;
for (int i = 0; i < NUM; i++) {
list->data[i] = 0; // 初始化所有元素为0
}
return list;
}

// ASCII码计算自动计算
int hash(int data) {
return data % NUM;
}

void put(HashList* list, char data) {
int index = hash(data);
printf("index = %d\n", index);
// 发生冲突
while (list->data[index] != 0) { // 确保这个位置已经被占用
int count = 1;
index = hash(index + 1); // 线性查找
count++;
printf("index = %d\n", index);

}
list->data[index] = data;
}

int main() {
HashList* list = initList();
put(list, 'A');
put(list, 'F');
for (int i = 0; i < NUM; i++) {
if (list->data[i] != 0) { // 只打印已经使用的槽位
printf("%c\n", list->data[i]);
}
}
return 0;
}

借鉴: https://github.com/hunterhug/goa.c/blob/master/algorithm/dict.md

go实现可变数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main

import (
"fmt"
"sync"
)

// Array 可变长数组
type Array struct {
array []int // 固定大小的数组,用满容量和满大小的切片来代替
len int // 真正长度
cap int // 容量
lock sync.Mutex // 为了并发安全使用的锁
}

// Make 新建一个可变长数组
func Make(len, cap int) *Array {
s := new(Array)
if len > cap {
panic("len large than cap")
}

// 把切片当数组用
array := make([]int, cap, cap)

// 元数据
s.array = array
s.cap = cap
s.len = 0
return s
}

// Append 增加一个元素
func (a *Array) Append(element int) {
// 并发锁
a.lock.Lock()
defer a.lock.Unlock()

// 大小等于容量,表示没多余位置了
if a.len == a.cap {
// 没容量,数组要扩容,扩容到两倍
newCap := 2 * a.len

// 如果之前的容量为0,那么新容量为1
if a.cap == 0 {
newCap = 1
}

newArray := make([]int, newCap, newCap)

// 把老数组的数据移动到新数组
for k, v := range a.array {
newArray[k] = v
}

// 替换数组
a.array = newArray
a.cap = newCap

}

// 把元素放在数组里
a.array[a.len] = element
// 真实长度+1
a.len = a.len + 1

}

// AppendMany 增加多个元素
func (a *Array) AppendMany(element ...int) {
for _, v := range element {
a.Append(v)
}

}

// Get 获取某个下标的元素
func (a *Array) Get(index int) int {
// 越界了
if a.len == 0 || index >= a.len {
panic("index over len")
}
return a.array[index]
}

// Len 返回真实长度
func (a *Array) Len() int {
return a.len
}

// Cap 返回容量
func (a *Array) Cap() int {
return a.cap
}

// Print 辅助打印
func Print(array *Array) (result string) {
result = "["
for i := 0; i < array.Len(); i++ {
// 第一个元素
if i == 0 {
result = fmt.Sprintf("%s%d", result, array.Get(i))
continue
}

result = fmt.Sprintf("%s %d", result, array.Get(i))
}
result = result + "]"
return
}

func main() {
// 创建一个容量为3的动态数组
a := Make(0, 3)
fmt.Println("cap", a.Cap(), "len", a.Len(), "array:", Print(a))

// 增加一个元素
a.Append(10)
fmt.Println("cap", a.Cap(), "len", a.Len(), "array:", Print(a))

// 增加一个元素
a.Append(9)
fmt.Println("cap", a.Cap(), "len", a.Len(), "array:", Print(a))

// 增加多个元素
a.AppendMany(8, 7)
fmt.Println("cap", a.Cap(), "len", a.Len(), "array:", Print(a))
}

字典
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package main

import (
"fmt"
"sync"
"unsafe"
)

// 集合结构体
type Set struct {
m map[int]struct{} // 用字典来实现,因为字段键不能重复
len int // 集合的大小
sync.RWMutex // 锁,实现并发安全
}

// 新建一个空集合
func NewSet(cap int64) *Set {
temp := make(map[int]struct{}, cap)
return &Set{
m: temp,
}
}

// 增加一个元素
func (s *Set) Add(item int) {
s.Lock()
defer s.Unlock()
s.m[item] = struct{}{} // 实际往字典添加这个键
s.len = len(s.m) // 重新计算元素数量
}

// 移除一个元素
func (s *Set) Remove(item int) {
s.Lock()
defer s.Unlock()

// 集合没元素直接返回
if s.len == 0 {
return
}

delete(s.m, item) // 实际从字典删除这个键
s.len = len(s.m) // 重新计算元素数量
}

// 查看是否存在元素
func (s *Set) Has(item int) bool {
s.RLock()
defer s.RUnlock()
_, ok := s.m[item]
return ok
}

// 查看集合大小
func (s *Set) Len() int {
return s.len
}

// 清除集合所有元素
func (s *Set) Clear() {
s.Lock()
defer s.Unlock()
s.m = map[int]struct{}{} // 字典重新赋值
s.len = 0 // 大小归零
}

// 集合是够为空
func (s *Set) IsEmpty() bool {
if s.Len() == 0 {
return true
}
return false
}

// 将集合转化为列表
func (s *Set) List() []int {
s.RLock()
defer s.RUnlock()
list := make([]int, 0, s.len)
for item := range s.m {
list = append(list, item)
}
return list
}

// 为什么使用空结构体
func other() {
a := struct{}{}
b := struct{}{}
if a == b {
fmt.Printf("right:%p\n", &a)
}

fmt.Println(unsafe.Sizeof(a))
}

func main() {
//other()

// 初始化一个容量为5的不可重复集合
s := NewSet(5)

s.Add(1)
s.Add(1)
s.Add(2)
fmt.Println("list of all items", s.List())

s.Clear()
if s.IsEmpty() {
fmt.Println("empty")
}

s.Add(1)
s.Add(2)
s.Add(3)

if s.Has(2) {
fmt.Println("2 does exist")
}

s.Remove(2)
s.Remove(3)
fmt.Println("list of all items", s.List())
}

加并发锁,实现线程安全,然后往结构体
s *Set 里面的内置 map 添加该元素:item,元素作为字典的键,会自动去重。同时,集合大小重新生成。

s.m[item] = struct{}{} 这行代码向 m 这个映射中添加一个 item 作为键,并将其值设置为 struct{}{}。
由于 m 是 map[int]struct{} 类型的,它的键是 int 类型,而值是空结构体 struct{} 类型,
它们不占用任何空间。这一步实际上是往字典添加一个键,而不需要关心值。

最大堆的建立

堆排序中的(递归)是制造最大堆的关键 其实也可以不用最大堆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<stdio.h>

void heapsort(int a[],int length,int i);
void buildheap(int a[], int length);
void swap(int* a, int* b);
void prinarry(int a[], int length);

void prinarry(int a[], int length)
{
for (int i = 0; i < length; i++)
{
printf("%d", a[i]);
}
printf("\n");
}

void swap(int* a, int* b)
{
int temp = 0;
temp=*a;
* a = *b;
* b = temp;

}
//重点在这里如何交换使得成为最大堆
void heapsort(int a[], int length, int i)
{
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;

if (left<length && a[left]>a[largest])
{
largest = left;
}
if (right<length && a[right]>a[largest])
{
largest = right;
}
if (largest != i)
{
swap(&a[i], &a[largest]);//必须加&
heapsort(a, length, largest);//这步递归是关键能确保整体保证是堆的性质
}

}

void buildheap(int a[], int length)//建立堆
{
int i = (length - 1) / 2;
for(i=(length-1)/2;i>=0;i--)
{
heapsort(a, length, i);
}

}

int main()
{
int arry[6] = { 2,4,7,9,5,8};
int length = sizeof(arry) / sizeof(arry[0]);
buildheap(arry, length);
prinarry(arry, length);
return 0;
}

好像不对劲 如果你期望的是排序后的数组(比如 120 100 90 50),你的代码缺少以下步骤:

交换堆顶和末尾元素:将最大堆的根节点(最大值)移动到数组末尾。
重新堆化剩余部分:对剩余的数组部分(即去掉最后一个元素的堆)重新堆化。
重复步骤:直到数组完全排序。

你的代码本身并没有严重的问题,但它仅仅完成了“构建最大堆”的功能,没有实现堆排序,因此输出的数组只是一个堆的结构(满足最大堆性质,但并非有序数组)。
如果你预期的结果是排序后的数组,那么确实还需要补充代码来完成堆排序的逻辑。

啊我靠

go实现最大堆
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// 一个最大堆,一棵完全二叉树
// 最大堆要求节点元素都不小于其左右孩子
type Heap struct {
// 堆的大小
Size int
// 使用内部的数组来模拟树
// 一个节点下标为 i,那么父亲节点的下标为 (i-1)/2
// 一个节点下标为 i,那么左儿子的下标为 2i+1,右儿子下标为 2i+2
Array []int
}

// 初始化一个堆
func NewHeap(array []int) *Heap {
h := new(Heap)
h.Array = array
return h
}

// 最大堆插入元素
func (h *Heap) Push(x int) {
// 堆没有元素时,使元素成为顶点后退出
if h.Size == 0 {
h.Array[0] = x
h.Size++
return
}

// i 是要插入节点的下标
i := h.Size

// 如果下标存在
// 将小的值 x 一直上浮
for i > 0 {
// parent为该元素父亲节点的下标
parent := (i - 1) / 2

// 如果插入的值小于等于父亲节点,那么可以直接退出循环,因为父亲仍然是最大的
if x <= h.Array[parent] {
break
}

// 否则将父亲节点与该节点互换,然后向上翻转,将最大的元素一直往上推
h.Array[i] = h.Array[parent]
i = parent
}

// 将该值 x 放在不会再翻转的位置
h.Array[i] = x

// 堆数量加一
h.Size++
}

// 最大堆移除根节点元素,也就是最大的元素
func (h *Heap) Pop() int {
// 没有元素,返回-1
if h.Size == 0 {
return -1
}

// 取出根节点
ret := h.Array[0]

// 因为根节点要被删除了,将最后一个节点放到根节点的位置上
h.Size--
x := h.Array[h.Size] // 将最后一个元素的值先拿出来
h.Array[h.Size] = ret // 将移除的元素放在最后一个元素的位置上

// 对根节点进行向下翻转,小的值 x 一直下沉,维持最大堆的特征
i := 0
for {
// a,b为下标 i 左右两个子节点的下标
a := 2*i + 1
b := 2*i + 2

// 左儿子下标超出了,表示没有左子树,那么右子树也没有,直接返回
if a >= h.Size {
break
}

// 有右子树,拿到两个子节点中较大节点的下标
if b < h.Size && h.Array[b] > h.Array[a] {
a = b
}

// 父亲节点的值都大于或等于两个儿子较大的那个,不需要向下继续翻转了,返回
if x >= h.Array[a] {
break
}

// 将较大的儿子与父亲交换,维持这个最大堆的特征
h.Array[i] = h.Array[a]

// 继续往下操作
i = a
}

// 将最后一个元素的值 x 放在不会再翻转的位置
h.Array[i] = x
return ret
}

这段代码太帅了 没有使用递归 用循环 构造了最大堆! 学习思想