CAndPrimer_17 关于堆栈,队列,树的基础理解(ADT)

本文深入探讨了使用C语言实现的数据结构,包括堆栈、队列和二叉搜索树的数组及链表实现方式,并提供了详细的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C和指针第十七章 一些数据结构的问题 笔记

  • Dargon

  • 2020/11/05

  • 所遇到的的重要的问题

  • 教科书 来自:《C And Primer》第十七章

  • 前记 :把这些简单的理解先记录在这,等待自己以后再次发现,再次更改或者升级,真好!

1.关于堆栈 Stack

堆栈的特点:先进后出,向上生长,主要的函数时POP and Push函数,实现进栈和出栈的操作

1.1用数组的形式实现

大致的描述,就是申请一个大数组,PUSH的话 就把数组下标向后移动一个(就是+1)操作;POP的话就直接数组向前一个(下标-1)操作
其实一个简单的基本堆栈数组实现思路就是这样:创造、增大、删除、入栈、出栈、栈顶、……

/* Dynamic array stack */
static STACK_TYPE *stack;
static size_t stack_size;
static int top_element =-1;
void create_stack( size_t size ) {
    assert( stack_size ==0 );
    stack_size =size;
    stack =malloc( size *sizeof(stack) );
    assert( stack !=NULL );
}

void resize_stack( size_t new_size ) {
    assert( new_size >top_element );
    stack =(STACK_TYPE *)realloc( stack, new_size *sizeof(STACK_TYPE) );
    assert( stack !=NULL );
}
void resize_stack_v2( size_t new_size ) {
    STACK_TYPE *old_stack;
    int i;
    assert( new_size >top_element );
    old_stack =stack;
    stack =(STACK_TYPE *)malloc( new_size *sizeof(STACK_TYPE) );
    assert( stack != NULL );
    for( i =0; i <=top_element; i++ ) {
        stack[i] =old_stack[i];
    }
    free(old_stack);
}
void destroy_stack( void ) {
    assert( stack_size >0 );
    stack_size =0;
    free(stack);
    stack =NULL;
}
void push( STACK_TYPE value ) {
    assert( ! is_full() );
    top_element +=1;
    stack[top_element] =value;
}
void pop( void ) {
    assert( ! is_empty );
    top_element -=1;
}
STACK_TYPE top( void ) {
    assert( ! is_empty() );
    return stack[top_element];
}
int is_empty( void ) {
    assert( stack_size >0 );
    return top_element ==-1;
}
int is_full( void ) {
    assert( stack_size >0 );
    return top_element ==stack_size -1;
}

1.2用链表的形式实现

就像一个单链表一样,你想PUSH 一个的话,我就在前面增加一个节点,这样的话就不存在,Created了,我是动态的,来一个增加一个的内存大小,可试想:原来是Stack指向堆栈,现在来一个new_node ,
这是关键思路:

new_node->value =value; 
new_node->next =stack; 
stack =new_node;

下面是出栈的关键思路:

first_node =stack;
stack =first_node->next;
free(stack);

整体的代码,关于Link-list 基本实现堆栈的思路

typedef struct STACK_NODE {
    STACK_TYPE value;
    struct STACK_NODE *next;
} StackNode;

static StackNode *stack;
void create_stack( size_t value ) {

}
void destroy_stack( void ) {
    while( !is_empty() ) 
        pop();
}
void push( STACK_TYPE value ) {
    StackNode *new_node;
    new_node =malloc( sizeof(StackNode) );
    assert( new_node !=NULL );
    new_node->value =value;
    new_node->next =stack; //接在前面
    stack =new_node;
}
void pop( void ) {
    StackNode *first_node;
    assert( first_node != NULL );
    first_node =stack;
    stack =first_node->next;
    free(first_node);
}
STACK_TYPE top( void ) {
    assert( !is_empty() );
    return stack->value;
}

/* justic the list empty, if empty return 1,else return 0
// how to use ,such as assert( !is_empty() );
 */
int is_empty( void ) {
    return (stack ==NULL);
}
int is_full( void ) {
    return FALSE;
}

2.关于队列 Queue

类似讲一个数组头尾连起来,形成一个圈,分别用front 和rear 指向队列的头和尾巴,insert插入一个的话,将尾巴Rear +1;同样delete 删除一个的话,就直接将front -1就可以了,但是……
在判断空 和 满的时候,会有异样的问题出现:空和满有一个状态是相同的rear 比 front 多1个
解决: 放置一个数组元素不用 。因此出现“假满”的状况
可以试验
当队列为空的时候,恰好满足 (rear +1) % QUEUE_SIZE == front
当队列为“假满"的时候,满足(rear +2) % QUEUE_SIZE == front
(通过自己简单的举例画一画,你能很清楚地了解到)

2.1用数组实现 Queue

#define QUEUE_SIZE 100
#define ARRAY_SIZE (QUEUE_SIZE +1)

static QUEUE_TYPE queue[ARRAY_SIZE];
static size_t front =1;
static size_t rear =0;
void create_queue( size_t size ) {

}
void destory_queue( void ) {

}
void insert( QUEUE_TYPE value ) {
    assert( !is_full_q );
    rear =(rear +1) %ARRAY_SIZE;
    queue[rear] =value;
}
void delete( void ) {
    assert( !is_empty_q );
    front =(front +1) %ARRAY_SIZE;
}
QUEUE_TYPE first( void ) {
    assert( !is_empty_q() );
    return queue[front];
}
int is_empty_q( void ) {
    return ( (rear +1) % ARRAY_SIZE == front );
}
int is_full_q( void ) {
    return ( (rear +2) & ARRAY_SIZE == front );
}

2.2用链表实现 Queue

用链表link-list 实现的比较有意思
实现insert 的时候,在rear 部插入
实现delete 时候,在front 部删除

typedef struct QUEUE_NODE {
    QUEUE_TYPE value;
    struct QUEUE_NODE *next;
} QueueNode;
static QueueNode *queue;
static QueueNode *front;
static QueueNode *rear;

void queue_insert( QUEUE_TYPE value ) {
    QueueNode *new_node;
    new_node =malloc( sizeof(QueueNode) );
    assert( new_node != NULL );
    new_node->value =value;
    new_node->next =NULL;
    if( rear ==NULL )
        front =new_node;
    else 
        rear->next =new_node; //接在后面
    rear =new_node;
}
void delete( void ) {
   QueueNode *next_node; 
   assert( ! is_empty_q() );
   next_node =front->next;
   free(front);
   front =next_node;
   if( front ==NULL )
        rear =NULL;
}

QUEUE_TYPE first( void ) {
    assert( ! is_empty_q() );
    return front->value;
}
int is_empty_q( void ) {
    return ( front ==NULL );
}
int is_full_q( void ) {
    return 0;
}

3.关于树 Tree

3.1简单的二叉搜索树(Binary search tree)BST

二叉搜索树,每个节点至多有两个孩子,left-child和 right-child 其中每个节点比它的左子树所有节点的值都要,但比它右子树的所有节点的值都要
其中最上面的节点问树根 Root 最小面没有节点的为叶子 leaf

3.2用数组实现 BST

整体是在一个大的数组里面,给出一个value 值,进行 insert的时候,在数组中找出value 的位置,直接进行放入即可
难点在于 delete:
要用到递归来实现,如果删除一个节点,找出左子树内最大的值的节点(一直往右子树进行找即可),将其删除,把值给current节点,这样就把节点间接的删除了。若是出现该节点的左子树没有 ,这时候就要找右子树那一串中最小值的节点,删除之后,再把最小值给current。对于最后的递归 其实只调用了一次,找到叶节点 delete之后,把值给current 。

static TREE_TYPE tree[ARRAY_SIZE];

static int left_child( int current ) {
    return ( current *2 );
}
static int right_child( int current ) {
    return ( current *2 +1 );
}
void insert( TREE_TYPE value ) {
    int current;
    assert( value !=0 );
    current =1; //For index

    while( tree[current] != 0 ) {
        if( value <tree[current] ) {
            current =left_child( current );
        }
        else {
            assert( value != tree[current] );
            current =right_child( current );
        }
        assert( current <ARRAY_SIZE );
    }
    tree[current] =value;
}
//Delete Function use recrusion//
void delete_tree( TREE_TYPE value ) {
    int current;
    int left;
    int right;
    int left_subtree_empty;
    int right_subtree_empty;

    current =1;
    //Seek for the location/place
    while( tree[current] !=value ) {
        if( value <tree[current] ) {
            current =left_child(current);
        }
        else {
            current =right_child(current);
        }
        assert( current <ARRAY_SIZE );
        assert( tree[current] !=0 );
    }
    //
    left =left_child( current );
    right =right_child( current );
    left_subtree_empty =(( left >ARRAY_SIZE ) || ( tree[left] ==0 ));
    right_subtree_empty =( ( right >ARRAY_SIZE) || ( tree[right] ==0 ) );
    //For leaf node have no chilenode
    if( left_subtree_empty && right_subtree_empty ) {
        tree[current] =0;
    }
    else {
        int this_child;
        int next_child;
        // have left node,so seek for the right biggest value
        if( !left_subtree_empty ) {
            this_child =left;
            next_child =right_child(this_child);

            while( next_child <ARRAY_SIZE && tree[next_child] != 0 ) {
                this_child =next_child;
                next_child =right_child( this_child );
            }           
        }
        // have rigth node, so seek for the left smallest value
        else {
            this_child =right;
            next_child =left_child(this_child);

            while( next_child <ARRAY_SIZE && tree[next_child] !=0 ) {
                this_child =next_child;
                next_child =left_child( this_child );
            }
        }
        value =tree[this_child];
        // Use recrusion to solve this question 一层一层进行剥开
        delete_tree(value);
        tree[current] =value;
    }
}
TREE_TYPE *find( TREE_TYPE value ) {
    int current;
    current =1;

    while( current <ARRAY_SIZE && tree[current] !=value ) {
        if( value <tree[current] ) {
            current =left_child( current );
        }
        else {
            current =right_child( current );
        }
    }
    if( current <ARRAY_SIZE ) 
        return (tree +current);
    else 
        return 0;
}

void callback( TREE_TYPE value ) {
    printf(" %d ", value);
}

static void do_pre_order_traverse( int current, void (*callback)( TREE_TYPE value ) ) {
    if( current <ARRAY_SIZE && tree[current] != 0 ) {
        callback(tree[current]);
        do_pre_order_traverse( left_child(current), callback );
        do_pre_order_traverse( right_child(current), callback );
    }
}

void pre_order_traverse( void (*callback)( TREE_TYPE value ) ) {
    do_pre_order_traverse( 1, callback );
}

3.3用链表实现 BST

包含一些其他函数 对于递归的调用应该好好理解

static TreeNode *tree;// The Big Tree Root

void insert_v1( TREE_TYPE value ) {
    TreeNode *current;
    TreeNode **link;

    link =&tree;
    while( (current =*link) != NULL ) {
        if( value <current->value ) {
            link =&(current->left);
        }
        else {
            assert( value != current->value );
            link =&(current->left);
        }
    }
    current =(TreeNode *)malloc( sizeof(TreeNode) );
    assert( current != NULL );
    current->value =value;
    current->left =current->right =NULL;
    *link =current;
}
static void do_destroy_tree(TreeNode *current) {
    if( current !=NULL ) {
        do_destroy_tree( current->left );
        do_destroy_tree( current->right );
        free(current);
    }
}
void destroy_tree() {
    do_destroy_tree( tree );
}

TREE_TYPE *find( TREE_TYPE value ) {
    TreeNode *current;
    current =tree;
    while( current !=NULL && current->value != value ) {
        if( value <current->value ) {
            current =current->left;
        }
        else {
            current =current->right;
        }
    }
    if( current != NULL ) {
        return &(current->value);
    }
    else {
        return NULL;
    }
}

void delete_treenode( TREE_TYPE value ) {
    TreeNode *current;
    TreeNode **link;
    link =&tree;
    while( (current =*link) !=NULL && current->value !=value ) {
        if( value <current->value ) {
            link =&(current->left);
        }
        else {
            link =&(current->right);
        }       
    }
    assert( current != NULL );

    if( current->left ==NULL && current->right ==NULL ) {
        *link =NULL;
        free(current);
    }
    else if( current->left ==NULL || current->right ==NULL ) {
        if( current->left !=NULL ) {
            *link =current->left; //上一个->left 指向当前current->left
        }
        else {
            *link =current->right; //上一个->right 指向当前current->right
        }
        free (current);
    }
    else {
        TreeNode *this_child;
        TreeNode *next_child;
        this_child =current->left;
        next_child =this_child->right;
        while( next_child != NULL ) {
            this_child =next_child;
            next_child =this_child->right;
        }
        value =this_child->value;
        delete_treenode(value);
        current->value =value;
    }
}

void callback( TREE_TYPE value ) {
    printf(" %d ", value);
}

static void do_pre_order_traverse_v1( TreeNode *current, void(* callback) (TREE_TYPE value) ) {
    if( current != NULL ) {
        callback( current->value );
        do_pre_order_traverse_v1( current->left, callback );
        do_pre_order_traverse_v1( current->right, callback );
    }
}

static void do_in_order_traverse_v1( TreeNode *current, void(* callback) (TREE_TYPE value) ) {
    if(current != NULL) {
        do_in_order_traverse_v1( current->left, callback );
        callback( current->value );
        do_in_order_traverse_v1( current->right, callback );
    }
}
static void do_post_order_traverse_v1( TreeNode *current, void(* callback) (TREE_TYPE value) ) {
    if(current != NULL) {
        do_post_order_traverse_v1( current->left, callback );
        do_post_order_traverse_v1( current->right, callback );
        callback( current->value );
    }
}
void pre_order_traverse_v1( void(* callback)(TREE_TYPE value) ) {
    do_pre_order_traverse_v1( tree, callback );
}
int count_nodes( TreeNode *tree ) {
    if( tree !=NULL ) return 0;
    return (1 +count_nodes(tree->left) +count_nodes(tree->right));
}
int number_of_nodes() {
    return count_nodes(tree);
}

int check_bst_subtree( TreeNode *node, int min, int max ) {
    if( node ==NULL ) 
        return TRUE;
    if( node->value <min && node->value >max ) 
        return FALSE;
    
    if( !(check_bst_subtree(node->left, min, node->value -1)) || //向左边找最大也只能是 (node->value -1) 小的可以很小
        !(check_bst_subtree(node->right, node->value +1, max)) );//向右边找最最小也只能是 (node->value +1) 大的可以很大
        return FALSE;
    return TRUE;
}
int check_bst_tree() {
    int min;
    int max;
    return check_bst_subtree( tree, min, max );
}

4.感悟

C和指针总算是看完了,有很多的点直接指到我之前学C 的时候的容易错乱的地方,发现是更稍微深入的理解 C 的特性,真好!
我认为语言这东西,这些比较基础经典的还是要常常翻一翻的,保持自己脑袋在用就行了,相信后面的学习会有更加不同的体会吧,加油 dargon

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值